java中spring的使用
Spring是一个开源框架,框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IoC)和面向切面(AOP)。
Spring 特点
①、方便解耦,简化开发
通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
②、AOP编程的支持
通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
③、声明式事务的支持
在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。
④、方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做的事情。例如:Spring对Junit4支持,可以通过注解方便的测试Spring程序。
⑤、方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,Hibernate、Hessian、Quartz)等的直接支持。
⑥、降低Java EE API的使用难度
Spring对很多难用的Java EE API(如JDBC,JavaMail,远程调用等)提供了一个薄薄的封装层,通过Spring的简易封装,这些Java EE API的使用难度大为降低。
⑦、Java 源码是经典学习范例
Spring的源码设计精妙、结构清晰、匠心独运,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。Spring框架源码无疑是Java技术的最佳实践范例。如果想在短时间内迅速提高自己的Java技术水平和应用开发水平,学习和研究Spring源码将会使你收到意想不到的效果。
Spring 框架结构
1、核心容器:核心容器提供 Spring 框架的基本功能(Spring Core)。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
2、Spring 上下文:Spring 上下文是一个配置文件,向 Spring框架提供上下文信息。Spring 上下文包括企业服务,例如JNDI、EJB、电子邮件、国际化、校验和调度功能。
3、Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
4、Spring DAO:JDBCDAO抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
5、Spring ORM:Spring 框架插入了若干个ORM框架,从而提供了 ORM 的对象关系工具,其中包括JDO、Hibernate和iBatisSQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
6、Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
7、Spring MVC 框架:MVC框架是一个全功能的构建 Web应用程序的 MVC 实现。通过策略接口,MVC框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。模型由javabean构成,存放于Map;视图是一个接口,负责显示模型;控制器表示逻辑代码,是Controller的实现。Spring框架的功能可以用在任何J2EE服务器中,大多数功能也适用于不受管理的环境。Spring 的核心要点是:支持不绑定到特定 J2EE服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同J2EE 环境(Web 或EJB)、独立应用程序、测试环境之间重用。
Spring 框架特征
轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。
控制反转——Spring通过一种称作控制反转(IoC)的技术促进了低耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
容器——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。
框架——Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。
MVC——Spring的作用是整合,但不仅仅限于整合,Spring 框架可以被看做是一个企业解决方案级别的框架。客户端发送请求,服务器控制器(由DispatcherServlet实现的)完成请求的转发,控制器调用一个用于映射的类HandlerMapping,该类用于将请求映射到对应的处理器来处理请求。HandlerMapping 将请求映射到对应的处理器Controller(相当于Action)在Spring 当中如果写一些处理器组件,一般实现Controller 接口,在Controller 中就可以调用一些Service 或DAO 来进行数据操作 ModelAndView 用于存放从DAO 中取出的数据,还可以存放响应视图的一些数据。 如果想将处理结果返回给用户,那么在Spring 框架中还提供一个视图组件ViewResolver,该组件根据Controller 返回的标示,找到对应的视图,将响应response 返回给用户。
Spring 优点
Spring能有效地组织你的中间层对象,无论你是否选择使用了EJB。如果你仅仅使用了Struts或其他的包含了J2EE特有APIs的framework,你会发现Spring关注了遗留下的问题。Spring能消除在许多工程上对Singleton的过多使用。根据我的经验,这是一个主要的问题,它减少了系统的可测试性和面向对象特性。
Spring能消除使用各种各样格式的属性定制文件的需要,在整个应用和工程中,可通过一种一致的方法来进行配置。曾经感到迷惑,一个特定类要查找迷幻般的属性关键字或系统属性,为此不得不读Javadoc乃至源编码吗?有了Spring,你可很简单地看到类的JavaBean属性。
Spring能通过接口而不是类促进好的编程习惯,减少编程代价到几乎为零。
Spring被设计为让使用它创建的应用尽可能少的依赖于他的APIs。在Spring应用中的大多数业务对象没有依赖于Spring。所以使用Spring构建的应用程序易于单元测试。
Spring能使EJB的使用成为一个实现选择,而不是应用架构的必然选择。你能选择用POJOs或local EJBs来实现业务接口,却不会影响调用代码。
Spring帮助你解决许多问题而无需使用EJB。Spring能提供一种EJB的替换物,它们适于许多web应用。例如,Spring能使用AOP提供声明性事务而不通过使用EJB容器,如果你仅仅需要与单个的数据库打交道,甚至不需要JTA实现。
Spring为数据存取提供了一致的框架,不论是使用JDBC或O/R mapping产品(如Hibernate)。
总结:
1.低侵入式设计,代码污染极低
2.独立于各种应用服务器,基于Spring框架的应用,可以真正实现Write Once,Run Anywhere的承诺
3.Spring的DI机制降低了业务对象替换的复杂性,提高了组件之间的解耦
4.Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行集中式管理,从而提供了更好的复用
5.Spring的ORM和DAO提供了与第三方持久层框架的良好整合,并简化了底层的数据库访问
6.Spring并不强制应用完全依赖于Spring,开发者可自由选用Spring框架的部分或全部
IOC-Inversion of Control,即控制反转。它不是什么技术,而是一种设计思想。
传统的创建对象的方法是直接通过 new 关键字,而 spring 则是通过 IOC 容器来创建对象,也就是说我们将创建对象的控制权交给了 IOC 容器。我们可以用一句话来概括 IOC:
IOC 让程序员不在关注怎么去创建对象,而是关注与对象创建之后的操作,把对象的创建、初始化、销毁等工作交给spring容器来做。
Spring 容器创建对象的三种方式
//这是测试对象,我们通过 IOC 来创建对象
public class HelloIoc {
public void sayHello(){
System.out.println("Hello IOC");
}
}
传统的创建对象的方法:new 关键字
//传统的创建对象方法----new
@Test
public void testTradition(){
HelloIoc hello = new HelloIoc();
hello.sayHello();
}
这里通过 Spring 容器怎么来创建呢?
第一种方法:利用默认的构造方法
在 src 目录下新建 applicationContext.xml 文件,这是 spring 配置文件,添加如下代码:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
创建对象的第一种方式:利用无参构造器
id:唯一标识符
class:类的全类名
-->
<bean id="helloIoc" class="com.my.ioc.HelloIoc" ></bean>
<!-- 别名属性 name:和bean的 id 属性对应 -->
<alias name="helloIoc" alias="helloIoc2"/>
</beans>
/**
* Spring 容器利用构造函数创建对象
*/
@Test
public void testCreateObjectByConstrutor(){
//1、启动 spring 容器
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
//2、从 spring 容器中取出数据
HelloIoc IOC = (HelloIoc) context.getBean("helloIoc");
//3、通过对象调用方法
IOC.sayHello();
//利用配置文件 alias 别名属性创建对象
HelloIoc IOC2 = (HelloIoc) context.getBean("helloIoc2");
IOC2.sayHello();
}
HelloIoc.java 中手动添加无参的构造方法,然后执行上面的测试代码,会发现构造方法会在 sayHello()方法执行之前调用。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="zhangsan" class="com.java.service.ZhangSan"></bean>
<bean id="lisi" class="com.java.service.Lisi"></bean>
<bean id="javaWork" class="com.java.service.JavaWork">
<property name="tester" ref="lisi"></property>
</bean>
</beans>
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("beans.xml");
JavaWork javaWork=(JavaWork)ac.getBean("javaWork");
javaWork.doTest();
}
public class JavaWork {
private Tester tester;
public void setTester(Tester tester) {
this.tester = tester;
}
public void doTest(){
/*ZhangSan zhangsan=new ZhangSan();
zhangsan.test();*/
tester.test();
}
}
第二种方法:利用静态工厂方法
首先创建静态工厂类 HelloStaticFactory.java
public class HelloStaticFactory {
public HelloStaticFactory(){
System.out.println("HelloStaticFactory constructor");
}
//静态工厂方法
public static HelloIoc getInstances(){
return new HelloIoc();
}
}
接着在 applicationContext.xml 中进行如下配置:
<!--
创建对象的第二种方式:利用静态工厂方法
factory-method:静态工厂类的获取对象的静态方法
class:静态工厂类的全类名
-->
<bean id="helloStaticFactory" factory-method="getInstances" class="com.my.ioc.HelloStaticFactory"></bean>
/**
* Spring 容器利用静态工厂方法创建对象
*/
@Test
public void createObjectStaticFactory(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
HelloIoc staticFactory =
(HelloIoc) context.getBean("helloStaticFactory");
staticFactory.sayHello();
}
spring容器只负责调用静态工厂方法,而这个静态工厂方法内部实现由程序员完成
利用实例工厂方法
首先创建实例工厂类 HelloInstanceFactory .java
public class HelloInstanceFactory {
public HelloInstanceFactory(){
System.out.println("实例工厂方法构造函数");
}
//利用实例工厂方法创建对象
public HelloIoc getInstance(){
HelloIoc instanceIoc = new HelloIoc();
return instanceIoc;
}
}
接着在 applicationContext.xml 中进行如下配置:
<!--
创建对象的第三种方式:利用实例工厂方法
factory-bean:指定当前Spring中包含工厂方法的beanID
factory-method:工厂方法名称
-->
<bean id="instanceFactory" class="com.my.ioc.HelloInstanceFactory"></bean>
<bean id="instance" factory-bean="instanceFactory" factory-method="getInstance"></bean>
/**
* Spring 容器利用实例工厂方法创建对象
*/
@Test
public void createObjectInstanceFactory(){
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
HelloIoc staticFactory =
(HelloIoc) context.getBean("instance");
staticFactory.sayHello();
}
Spring 容器创建对象的时机
默认情况下,启动 spring 容器便创建对象
在spring的配置文件bean中有一个属性 lazy-init="default/true/false"
①、如果lazy-init为"default/false"在启动spring容器时创建对象(默认情况)
②、如果lazy-init为"true",在context.getBean时才要创建对象
<bean id="helloIoc" lazy-init=“true” class="com.my.ioc.HelloIoc" ></bean>
在第一种情况下可以在启动spring容器的时候,检查spring容器配置文件的正确性,如果再结合tomcat,如果spring容器不能正常启动,整个tomcat就不能正常启动。但是这样的缺点是把一些bean过早的放在了内存中,如果有数据,则对内存来是一个消耗。
反过来,在第二种情况下,可以减少内存的消耗,但是不容易发现错误
spring的bean中的scope:"singleton/prototype/request/session/global session"
一、默认scope的值是singleton,即产生的对象是单例的
applicationContext.xml 文件中配置:
<bean id="helloIoc" scope="singleton" class="com.my.ioc.HelloIoc" ></bean>
//spring 容器默认产生对象是单例的 scope="singleton"
@Test
public void test_scope_single_CreateObject(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloIoc hello1 = (HelloIoc) context.getBean("helloIoc");
HelloIoc hello2 = (HelloIoc) context.getBean("helloIoc");
System.out.println(hello1.equals(hello2)); //true
}
scope=“prototype”
多例模式,并且spring容器启动的时候并不会创建对象,而是在得到 bean 的时候才会创建对象
applicationContext.xml 文件中配置:
总结:在单例模式下,启动 spring 容器,便会创建对象;在多例模式下,启动容器并不会创建对象,获得 bean 的时候才会创建对象
scope=“request” 每次HTTP请求都会创建一个新的bean
scope=“session”同一个HTTP Session共享一个Bean
scope=“global session” 同一个全局Session共享一个Bean,一般用于portlet应用环境
scope=“application”同一个Application 共享一个Bean
Spring 容器生命周期
/**
* Spring 容器的生命周期
* @author hadoop
*
*/
public class SpringLifeCycle {
public SpringLifeCycle(){
System.out.println("SpringLifeCycle");
}
//定义初始化方法
public void init(){
System.out.println("init...");
}
//定义销毁方法
public void destroy(){
System.out.println("destroy...");
}
public void sayHello(){
System.out.println("say Hello...");
}
}
applicationContext.xml
<!-- 生命周期 -->
<bean id="springLifeCycle" init-method="init" destroy-method="destroy" class="com.my.ioc.SpringLifeCycle"></bean>
public void testSpringLifeCycle(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
SpringLifeCycle hello = (SpringLifeCycle) context.getBean("springLifeCycle");
hello.sayHello();
//销毁spring容器
ClassPathXmlApplicationContext classContext = (ClassPathXmlApplicationContext) context;
classContext.close();
}
spring 容器的声明周期
1、spring容器创建对象
2、执行init方法
3、调用自己的方法
4、当spring容器关闭的时候执行destroy方法
注意:当scope为"prototype"时,调用 close() 方法时是不会调用 destroy 方法的
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class T {
private ApplicationContext ac;
@Before
public void setUp() throws Exception {
ac=new ClassPathXmlApplicationContext("beans.xml");
}
@Test
public void test1() {
People people=(People)ac.getBean("people1");
People people2=(People)ac.getBean("people1");
System.out.println(people.getDog()==people2.getDog());
System.out.println(ac.getBean("dog")==ac.getBean("dog"));
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dog" class="com.java1234.entity.Dog" scope="prototype">
<property name="name" value="Jack"></property>
</bean>
<bean id="people1" class="com.java.entity.People">
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
<property name="age" value="11"></property>
<lookup-method name="getDog" bean="dog"/>//方法动态注入
</bean>
</beans>
public abstract class People {
private int id;
private String name;
private int age;
private Dog dog;
public abstract Dog getDog();
public void setDog(Dog dog) {
this.dog = dog;
}
@Override
public String toString() {
return "People [id=" + id + ", name=" + name + ", age=" + age
+ ", dog=" + dog.getName() + "]";
}
}
方法替换
public class People {
private int id;
private String name;
private int age;
private Dog dog;
public Dog getDog() {
Dog dog=new Dog();
dog.setName("Jack");
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
}
public class People2 implements MethodReplacer {
@Override
public Object reimplement(Object arg0, Method arg1, Object[] arg2)
throws Throwable {
Dog dog=new Dog();
dog.setName("Tom");
return dog;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="people1" class="com.java.entity.People">
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
<property name="age" value="11"></property>
<replaced-method name="getDog" replacer="people2"></replaced-method>
</bean>
<bean id="people2" class="com.java.entity.People2"></bean>
</beans>
DI依赖注入
spring动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class Person {
private Long pid;
private String pname;
private Student students;
private List lists;
private Set sets;
private Map maps;
private Properties properties;
}
在 applicationContext.xml 中进行赋值
<!--
property是用来描述一个类的属性
基本类型的封装类、String等需要值的类型用value赋值
引用类型用ref赋值
-->
<bean id="person" class="com.my.di.Person">
<property name="pid" value="1"></property>
<property name="pname" value="vae"></property>
<property name="students">
<ref bean="student"/>
</property>
<property name="students" ref="student">//注入bean
<property name="students.name" value="jack">//级联private Student students=new Student()
</property>
<property name="lists">
<list>
<value>1</value>
<ref bean="student"/>
<value>vae</value>
</list>
</property>
<property name="sets">
<set>
<value>1</value>
<ref bean="student"/>
<value>vae</value>
</set>
</property>
<property name="maps">
<map>
<entry key="m1" value="1"></entry>
<entry key="m2" >
<ref bean="student"/>
</entry>
</map>
</property>
<property name="properties">
<props>
<prop key="p1">p1</prop>
<prop key="p2">p2</prop>
</props>
</property>
</bean>
<bean id="student" class="com.my.di.Student"></bean>
//利用 set 方法给对象赋值
@Test
public void testSet(){
//1、启动 spring 容器
//2、从 spring 容器中取出数据
//3、通过对象调用方法
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = (Person) context.getBean("person");
System.out.println(person.getPname());//vae
}
利用 构造函数 给属性赋值
在实体类 Person.java 中添加两个构造方法:有参和无参
//默认构造函数
public Person(){}
//带参构造函数
public Person(Long pid,Student students){
this.pid = pid;
this.students = students;
}
在 applicationContext.xml 中进行赋值
<!-- 根据构造函数赋值 -->
<!--
index 代表参数的位置 从0开始计算
type 指的是参数的类型,在有多个构造函数时,可以用type来区分,要是能确定是那个构造函数,可以不用写type
value 给基本类型赋值
ref 给引用类型赋值
-->
<bean id="person_con" class="com.my.di.Person">
<constructor-arg index="0" type="java.lang.Long" value="1">
</constructor-arg>
<constructor-arg index="1" type="com.my.di.Student" ref="student_con"></constructor-arg>
</bean>
<bean id="student_con" class="com.my.di.Student"></bean>
//利用 构造函数 给对象赋值
@Test
public void testConstrutor(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = (Person) context.getBean("person_con");
System.out.println(person.getPid());//1
}
1、如果spring的配置文件中的bean中没有<constructor-arg>该元素,则调用默认的构造函数
2、如果spring的配置文件中的bean中有<constructor-arg>该元素,则该元素确定唯一的构造函数
构造方法通过类型注入
public class People {
private int id;
private String name;
private int age;
public People() {
super();
// TODO Auto-generated constructor stub
}
public People(int id, String name, int age) {
super();
this.id = id;
this.name = name;
this.age = age;
}
}
<bean id="people3" class="com.java.entity.People">
<constructor-arg type="int" value="2"></constructor-arg>
<constructor-arg type="String" value="李四"></constructor-arg>
<constructor-arg type="int" value="22"></constructor-arg>
</bean>
自动装配
byName通过名称自动匹配
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byName">
<bean id="dog" class="com.java.entity.Dog">
<property name="name" value="Jack"></property>
</bean>
<bean id="dog2" class="com.java.entity.Dog">
<property name="name" value="Tom"></property>
</bean>
<bean id="people1" class="com.java.entity.People">
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
<property name="age" value="11"></property>
</bean>
</beans>
public class People {
private int id;
private String name;
private int age;
private Dog dog;
}
byType:通过类型进行自动匹配
constructor;根据类型,自动注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="constructor">
<bean id="dog" class="com.java.entity.Dog">
<property name="name" value="Jack"></property>
</bean>
<bean id="people1" class="com.java.entity.People">
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
<property name="age" value="11"></property>
</bean>
</beans>
public class People {
private int id;
private String name;
private int age;
private Dog dog;
public People() {
super();
// TODO Auto-generated constructor stub
}
public People(Dog dog) {
super();
System.out.println("constructor");
this.dog = dog;
}
}
bean之间的关系
继承
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dog" class="com.java.entity.Dog">
<property name="name" value="jack"></property>
</bean>
<bean id="abstractPeople" class="com.java.entity.People" abstract="true">
<property name="className" value="高三5班"></property>
<property name="age" value="19"></property>
</bean>
<bean id="zhangsan" parent="abstractPeople" depends-on="autority">
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
</bean>
<bean id="lisi" parent="abstractPeople">
<property name="id" value="2"></property>
<property name="name" value="李四"></property>
<property name="age" value="20"></property>
<property name="dog" ref="dog"></property>
</bean>
<bean id="autority" class="com.java.service.Authority"></bean>
</beans>
public class T {
private ApplicationContext ac;
@Before
public void setUp() throws Exception {
ac=new ClassPathXmlApplicationContext("beans.xml");
}
@Test
public void test1() {
People zhangsan=(People)ac.getBean("zhangsan");
System.out.println(zhangsan);
People lisi=(People)ac.getBean("lisi");
System.out.println(lisi);
}
}
依赖
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dog" class="com.java.entity.Dog">
<property name="name" value="jack"></property>
</bean>
<bean id="zhangsan" parent="abstractPeople" depends-on="autority">
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
</bean>
<bean id="lisi" parent="abstractPeople">
<property name="id" value="2"></property>
<property name="name" value="李四"></property>
<property name="age" value="20"></property>
<property name="dog" ref="dog"></property>
</bean>
<bean id="autority" class="com.java.service.Authority"></bean>
</beans>
使用注解,让 Spring 容器帮我们产生 Person 对象
使用注解
在 applicationContext.xml 中引入命名空间
引入的命名空间,简单来说就是用来约束xml文件格式的。第一个 xmlns:context ,这表示标签格式应该是 <context:标
在 applicationContext.xml 文件中引入注解扫描器
<!-- 组件扫描,扫描含有注解的类 -->
<context:component-scan base-package="com.my.annotation"></context:component-scan>
base-package:表示含有注解类的包名
如果扫描多个包,则上面的代码书写多行,改变 base-package 里面的内容即可!
@Component
如果一个类上加了@Component注解,就会进行如下的法则
如果其value属性的值为""
@Component
public class Person {}
等价于
<bean id="person" class="..Person">
如果其value属性的值不为""
@Component("p")
public class Person {}
等价于
<bean id="p" class="..Person">
在 Person 类中添加注解@Component
public void testAnnotation(){
//1、启动 spring 容器
//2、从 spring 容器中取出数据
//3、通过对象调用方法
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = (Person) context.getBean("person");
System.out.println(person.getPname());
}
@Repository :dao层
@Service:service层
@Controller:web层
注解 @Resource
@Resource 注解,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 通过 @Resource 的使用来消除 set ,get方法。
@Resource注解以后,判断该注解name的属性是否为""(name没有写)
①、如果没有写name属性,则会让属性的名称的值和spring配置文件bean中ID的值做匹配(如果没有进行配置,也和注解@Component进行匹配),如果匹配成功则赋值,如果匹配不成功,则会按照spring配置文件class类型进行匹配,如果匹配不成功,则报错
②、如果有name属性,则会按照name属性的值和spring的bean中ID进行匹配,匹配成功,则赋值,不成功则报错
不使用注解:
<property name="students">
<ref bean="student"/>
</property>
<bean id="student" class="com.my.annotation_di.Student"></bean>
使用注解:
注解 @Autowired
功能和注解 @Resource 一样,可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。只不过注解@Resource 是按照名称来进行装配,而@Autowired 则是按照类型来进行装配。
在使用@Autowired时,首先在容器中查询对应类型的bean
如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据
如果查询的结果不止一个,那么@Autowired会根据名称来查找。
如果查询的结果为空,那么会抛出异常。解决方法时,使用required=false
创建接口 PersonDao
public interface PersonDao {
public void savePerson();
}
创建一个接口实现类 PersonDaoImplOne
import org.springframework.stereotype.Component;
@Component("personDaoImplOne")
public class PersonDaoImplOne implements PersonDao{
@Override
public void savePerson() {
System.out.println("save Person One");
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("personService")
public class PersonService{
@Autowired
private PersonDao personDao;
public void savePerson() {
this.personDao.savePerson();
}
}
注意:这里我们在 private PesronDao personDao 上面添加了注解 @Autowired,它首先会根据类型去匹配,PersonDao 是一个接口,它的实现类是 PesronDaoImpOne,那么这里的意思就是:
PersonDao personDao = new PersonDaoImpOne();
那么问题来了,如果 PersonDao 的实现类有多个呢?我们创建第一个实现类 PersonDaoImpTwo
第一种方法:更改名称
第二种方法:@Autowired 和 @Qualifier("名称") 配合使用
AOP(Aspect Oriented Programming),通常称为面向切面编程。它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
静态代理
增加和删除操作都必须要开启事务,操作完成之后要提交事务。
public interface UserService {
//添加 user
public void addUser(User user);
//删除 user
public void deleteUser(int uid);
}
创建 UserService的实现类
public class UserServiceImpl implements UserService{
@Override
public void addUser(User user) {
System.out.println("增加 User");
}
@Override
public void deleteUser(int uid) {
System.out.println("删除 User");
}
}
创建事务类 MyTransaction
public class MyTransaction {
//开启事务
public void before(){
System.out.println("开启事务");
}
//提交事务
public void after(){
System.out.println("提交事务");
}
}
创建代理类 ProxyUser.java
public class ProxyUser implements UserService{
//真实类
private UserService userService;
//事务类
private MyTransaction transaction;
//使用构造函数实例化
public ProxyUser(UserService userService,MyTransaction transaction){
this.userService = userService;
this.transaction = transaction;
}
@Override
public void addUser(User user) {
transaction.before();
userService.addUser(user);
transaction.after();
}
@Override
public void deleteUser(int uid) {
transaction.before();
userService.deleteUser(uid);
transaction.after();
}
}
@Test
public void testOne(){
MyTransaction transaction = new MyTransaction();
UserService userService = new UserServiceImpl();
//产生静态代理对象
ProxyUser proxy = new ProxyUser(userService, transaction);
proxy.addUser(null);
proxy.deleteUser(0);
}
业务类UserServiceImpl 只需要关注业务逻辑本身,保证了业务的重用性,这也是代理类的优点
这样写的缺点:
①、代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
②、如果接口增加一个方法,比如 UserService 增加修改 updateUser()方法,则除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
使用JDK动态代理
动态代理就不要自己手动生成代理类了,我们去掉 ProxyUser.java 类,增加一个 ObjectInterceptor.java 类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.my.aop.one.MyTransaction;
public class ObjectInterceptor implements InvocationHandler{
//目标类
private Object target;
//切面类(这里指事务类)
private MyTransaction transaction;
//通过构造器赋值
public ObjectInterceptor(Object target,MyTransaction transaction){
this.target = target;
this.transaction = transaction;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//开启事务
this.transaction.before();
//调用目标类方法
method.invoke(this.target, args);
//提交事务
this.transaction.after();
return null;
}
}
@Test
public void testOne(){
//目标类
Object target = new UserServiceImpl();
//事务类
MyTransaction transaction = new MyTransaction();
ObjectInterceptor proxyObject = new ObjectInterceptor(target, transaction);
/**
* 三个参数的含义:
* 1、目标类的类加载器
* 2、目标类所有实现的接口
* 3、拦截器
*/
UserService userService = (UserService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), proxyObject);
userService.addUser(null);
}
后期在 UserService 中增加业务方法,都不用更改代码就能自动给我们生成代理对象。而且将 UserService 换成别的类也是可以的。
也就是做到了代理对象能够代理多个目标类,多个目标方法。
注意:我们这里使用的是 JDK 动态代理,要求是必须要实现接口。与之对应的另外一种动态代理实现模式 Cglib,则不需要,我们这里就不讲解 cglib 的实现方式了。
AOP 关键术语
1.target:目标类,需要被代理的类。例如:UserService
2.Joinpoint(连接点):所谓连接点是指那些可能被拦截到的方法。例如:所有的方法
3.PointCut 切入点:已经被增强的连接点。例如:addUser()
4.advice 通知/增强,增强代码。例如:after、before
5. Weaving(织入):是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.
6.proxy 代理类:通知+切入点
7. Aspect(切面): 是切入点pointcut和通知advice的结合
具体可以根据下面这张图来理解:
AOP 的通知类型
Spring按照通知Advice在目标类方法的连接点位置,可以分为5类
- 前置通知 org.springframework.aop.MethodBeforeAdvice
- 在目标方法执行前实施增强,比如上面例子的 before()方法
- 后置通知 org.springframework.aop.AfterReturningAdvice
- 在目标方法执行后实施增强,比如上面例子的 after()方法
- 环绕通知 org.aopalliance.intercept.MethodInterceptor
- 在目标方法执行前后实施增强
- 异常抛出通知 org.springframework.aop.ThrowsAdvice
- 在方法抛出异常后实施增强
- 引介通知 org.springframework.aop.IntroductionInterceptor
在目标类中添加一些新的方法和属性
我们只需要在Spring 的配置文件 applicationContext.xml 进行如下配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--1、 创建目标类 -->
<bean id="userService" class="com.my.aop.UserServiceImpl"></bean>
<!--2、创建切面类(通知) -->
<bean id="transaction" class="com.my.aop.one.MyTransaction"></bean>
<!--3、aop编程
3.1 导入命名空间
3.2 使用 <aop:config>进行配置
proxy-target-class="true" 声明时使用cglib代理
如果不声明,Spring 会自动选择cglib代理还是JDK动态代理
<aop:pointcut> 切入点 ,从目标对象获得具体方法
<aop:advisor> 特殊的切面,只有一个通知 和 一个切入点
advice-ref 通知引用
pointcut-ref 切入点引用
3.3 切入点表达式
execution(* com.my.aop.*.*(..))
选择方法 返回值任意 包 类名任意 方法名任意 参数任意
-->
<aop:config>
<!-- 切入点表达式 -->
<aop:pointcut expression="execution(* com.my.aop.*.*(..))" id="myPointCut"/>
<aop:aspect ref="transaction">
<!-- 配置前置通知,注意 method 的值要和 对应切面的类方法名称相同 -->
<aop:before method="before" pointcut-ref="myPointCut"></aop:before>
<aop:after-returning method="after" pointcut-ref="myPointCut"/>
</aop:aspect>
</aop:config>
</beans>
@Test
public void testAop(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService useService = (UserService) context.getBean("userService");
useService.addUser(null);
}
①、 切入点表达式,一个完整的方法表示如下:
execution(modifiers-pattern? ref-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
类修饰符 返回值 方法所在的包 方法名 方法抛出的异常
那么根据上面的对比,我们就很好理解:
execution(* com.my.aop.*.*(..))
选择方法 返回值任意 包 类名任意 方法名任意 参数任意
那么它表达的意思是 返回值任意,包名为 com.ys.aop 下的任意类名中的任意方法名,参数任意。
如果切入点表达式有多个不同目录呢?
<aop:pointcut expression="execution(* com.my.*Service1.*(..)) ||
execution(* com.my.*Service2.*(..))" id="myPointCut"/>
表示匹配 com.my包下的,以 Service1结尾或者以Service2结尾的类的任意方法。
AOP 切入点表达式支持多种形式的定义规则:
1、execution:匹配方法的执行(常用)
execution(public *.*(..))
2.within:匹配包或子包中的方法(了解)
within(com.my.aop..*)
3.this:匹配实现接口的代理对象中的方法(了解)
this(com.my.aop.user.UserDAO)
4.target:匹配实现接口的目标对象中的方法(了解)
target(com.my.aop.user.UserDAO)
5.args:匹配参数格式符合标准的方法(了解)
args(int,int)
6.bean(id) 对指定的bean所有的方法(了解)
bean('userServiceId')
②、springAOP 的具体加载步骤:
1、当 spring 容器启动的时候,加载了 spring 的配置文件
2、为配置文件中的所有 bean 创建对象
3、spring 容器会解析 aop:config 的配置
1、解析切入点表达式,用切入点表达式和纳入 spring 容器中的 bean 做匹配
如果匹配成功,则会为该 bean 创建代理对象,代理对象的方法=目标方法+通知
如果匹配不成功,不会创建代理对象
4、在客户端利用 context.getBean() 获取对象时,如果该对象有代理对象,则返回代理对象;如果没有,则返回目标对象
说明:如果目标类没有实现接口,则 spring 容器会采用 cglib 的方式产生代理对象,如果实现了接口,则会采用 jdk 的方式
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class StudentServiceAspect {
public void doBefore(JoinPoint jp){
System.out.println("类名:"+jp.getTarget().getClass().getName());
System.out.println("方法名:"+jp.getSignature().getName());
System.out.println("开始添加学生:"+jp.getArgs()[0]);
}
public void doAfter(JoinPoint jp){
System.out.println("类名:"+jp.getTarget().getClass().getName());
System.out.println("方法名:"+jp.getSignature().getName());
System.out.println("学生添加完成:"+jp.getArgs()[0]);
}
public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("添加学生前");
Object retVal=pjp.proceed();
System.out.println(retVal);
System.out.println("添加学生后");
return retVal;
}
public void doAfterReturning(JoinPoint jp){
System.out.println("返回通知");
}
public void doAfterThrowing(JoinPoint jp,Throwable ex){
System.out.println("异常通知");
System.out.println("异常信息:"+ex.getMessage());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="studentServiceAspect" class="com.java.advice.StudentServiceAspect"></bean>
<bean id="studentService" class="com.java.service.impl.StudentServiceImpl"></bean>
<aop:config>
<aop:aspect id="studentServiceAspect" ref="studentServiceAspect">
<aop:pointcut expression="execution(* com.java.service.*.*(..))" id="businessService"/>
<aop:before method="doBefore" pointcut-ref="businessService"/>
<aop:after method="doAfter" pointcut-ref="businessService"/>
<aop:around method="doAround" pointcut-ref="businessService"/>
<aop:after-returning method="doAfterReturning" pointcut-ref="businessService"/>
<aop:after-throwing method="doAfterThrowing" pointcut-ref="businessService" throwing="ex"/>
</aop:aspect>
</aop:config>
</beans>
public class StudentServiceImpl implements StudentService{
@Override
public void addStudent(String name) {
// System.out.println("开始添加学生"+name);
System.out.println("添加学生"+name);
System.out.println(1/0);
// System.out.println("完成学生"+name+"的添加");
}
}
AspectJ是一个面向切面的框架,它扩展了Java语言。
Aspect 通知类型,定义了类型名称以及方法格式。类型如下:
before:前置通知(应用:各种校验)
在方法执行前执行,如果通知抛出异常,阻止方法运行
afterReturning:后置通知(应用:常规数据处理)
方法正常返回后执行,如果方法中抛出异常,通知无法执行
必须在方法执行后才执行,所以可以获得方法的返回值。
around:环绕通知(应用:十分强大,可以做任何事情)
方法执行前后分别执行,可以阻止方法的执行
必须手动执行目标方法
afterThrowing:抛出异常通知(应用:包装异常信息)
方法抛出异常后执行,如果方法没有抛出异常,无法执行
after:最终通知(应用:清理现场)
方法执行完毕后执行,无论方法中是否出现异常
这里最重要的是around,环绕通知,它可以代替上面的任意通知。
在程序中表示的意思如下:
try{
//前置:before
//手动执行目标方法
//后置:afterRetruning
} catch(){
//抛出异常 afterThrowing
} finally{
//最终 after
}
AOP具体实例
public interface UserService {
//添加 user
public void addUser();
//删除 user
public void deleteUser();
}
创建实现类
public class UserServiceImpl implements UserService{
@Override
public void addUser() {
System.out.println("增加 User");
}
@Override
public void deleteUser() {
System.out.println("删除 User");
}
}
创建切面类(包含各种通知)
import org.aspectj.lang.JoinPoint;
public class MyAspect {
/**
* JoinPoint 能获取目标方法的一些基本信息
* @param joinPoint
*/
public void myBefore(JoinPoint joinPoint){
System.out.println("前置通知 : " + joinPoint.getSignature().getName());
}
public void myAfterReturning(JoinPoint joinPoint,Object ret){
System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret);
}
public void myAfter(){
System.out.println("最终通知");
}
}
创建spring配置文件applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--1、 创建目标类 -->
<bean id="userService" class="com.my.aop.UserServiceImpl"></bean>
<!--2、创建切面类(通知) -->
<bean id="myAspect" class="com.my.aop.MyAspect"></bean>
<!--3、aop编程
3.1 导入命名空间
3.2 使用 <aop:config>进行配置
proxy-target-class="true" 声明时使用cglib代理
如果不声明,Spring 会自动选择cglib代理还是JDK动态代理
<aop:pointcut> 切入点 ,从目标对象获得具体方法
<aop:advisor> 特殊的切面,只有一个通知 和 一个切入点
advice-ref 通知引用
pointcut-ref 切入点引用
3.3 切入点表达式
execution(* com.my.aop.*.*(..))
选择方法 返回值任意 包 类名任意 方法名任意 参数任意
-->
<aop:config>
<aop:aspect ref="myAspect">
<!-- 切入点表达式 -->
<aop:pointcut expression="execution(* com.my.aop.*.*(..))" id="myPointCut"/>
<!-- 3.1 前置通知
<aop:before method="" pointcut="" pointcut-ref=""/>
method : 通知,及方法名
pointcut :切入点表达式,此表达式只能当前通知使用。
pointcut-ref : 切入点引用,可以与其他通知共享切入点。
通知方法格式:public void myBefore(JoinPoint joinPoint){
参数1:org.aspectj.lang.JoinPoint 用于描述连接点(目标方法),获得目标方法名等
-->
<aop:before method="myBefore" pointcut-ref="myPointCut"/>
<!-- 3.2后置通知 ,目标方法后执行,获得返回值
<aop:after-returning method="" pointcut-ref="" returning=""/>
returning 通知方法第二个参数的名称
通知方法格式:public void myAfterReturning(JoinPoint joinPoint,Object ret){
参数1:连接点描述
参数2:类型Object,参数名 returning="ret" 配置的
-->
<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" />
<!-- 3.3 最终通知 -->
<aop:after method="myAfter" pointcut-ref="myPointCut"/>
</aop:aspect>
</aop:config>
</beans>
@Test
public void testAop(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService useService = (UserService) context.getBean("userService");
useService.addUser();
}
测试异常通知
public void addUser() {
int i = 1/0;//显然这里会抛出除数不能为 0
System.out.println("增加 User");
}
切面类
public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("抛出异常通知 : " + e.getMessage());
}
<!-- 3.4 抛出异常
<aop:after-throwing method="" pointcut-ref="" throwing=""/>
throwing :通知方法的第二个参数名称
通知方法格式:public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
参数1:连接点描述对象
参数2:获得异常信息,类型Throwable ,参数名由throwing="e" 配置
-->
<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>
@Test
public void testAop(){
String str = "com/my/execption/applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(str);
UserService useService = (UserService) context.getBean("userService");
useService.addUser();
}
测试环绕通知
public class MyAspect {
public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("前置通知");
//手动执行目标方法
Object obj = joinPoint.proceed();
System.out.println("后置通知");
return obj;
}
}
applicationContext.xml 配置如下:
通过 xml 配置的方式。
<!-- 环绕通知
<aop:around method="" pointcut-ref=""/>
通知方法格式:public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
返回值类型:Object
方法名:任意
参数:org.aspectj.lang.ProceedingJoinPoint
抛出异常
执行目标方法:Object obj = joinPoint.proceed();
-->
<aop:around method="myAround" pointcut-ref="myPointCut"/>
@Test
public void testAop(){
String str = "com/my/around/applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(str);
UserService useService = (UserService) context.getBean("userService");
useService.addUser();
}
注解实现 AOP
@Aspect 声明切面,修饰切面类,从而获得 通知。
通知
@Before 前置
@AfterReturning 后置
@Around 环绕
@AfterThrowing 抛出异常
@After 最终
切入点
@PointCut ,修饰方法 private void xxx(){} 之后通过“方法名”获得切入点引用
在 applicationContext.xml 文件中导入相应的命名空间
<?xml version="1.0" encoding="UTF-8"?>
<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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
注解配置 bean
xml配置:
<!--1、创建目标类 -->
<bean id="userService" class="com.my.aop.UserServiceImpl"></bean>
<!--2、创建切面类(通知) -->
<bean id="myAspect" class="com.my.aop.MyAspect"></bean>
配置扫描注解识别
在 applicationContext.xml 文件中添加如下配置:
<!-- 配置扫描注解类
base-package:表示含有注解类的包名。
如果扫描多个包,则下面的代码书写多行,改变 base-package 里面的内容即可!
-->
<context:component-scan base-package="com.my.aop"></context:component-scan>
在切面类上添加 @Aspect 注解,如下:
告诉 Spring 哪个是切面类
如何让 Spring 认识我们所配置的 AOP 注解呢
我们在 applicationContext.xml 文件中增加如下配置:
<!--2、确定 aop 注解生效 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
注解配置前置通知
我们先看 xml 配置前置通知如下:
<!-- 切入点表达式 -->
<aop:pointcut expression="execution(* com.my.aop.*.*(..))" id="myPointCut"/>
<!-- 3.1 前置通知
<aop:before method="" pointcut="" pointcut-ref=""/>
method : 通知,及方法名
pointcut :切入点表达式,此表达式只能当前通知使用。
pointcut-ref : 切入点引用,可以与其他通知共享切入点。
通知方法格式:public void myBefore(JoinPoint joinPoint){
参数1:org.aspectj.lang.JoinPoint 用于描述连接点(目标方法),获得目标方法名等
-->
<aop:before method="myBefore" pointcut-ref="myPointCut"/>
那么注解的方式如下:
注解配置后置通知
xml 配置后置通知:
<!-- 3.2后置通知 ,目标方法后执行,获得返回值
<aop:after-returning method="" pointcut-ref="" returning=""/>
returning 通知方法第二个参数的名称
通知方法格式:public void myAfterReturning(JoinPoint joinPoint,Object ret){
参数1:连接点描述
参数2:类型Object,参数名 returning="ret" 配置的
-->
<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" />
注意看,后置通知有个 returning="ret" 配置,这是用来获得目标方法的返回值的。
public void testAopAnnotation(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext_Annotation.xml");
UserService useService = (UserService) context.getBean("userService");
useService.addUser();
useService.deleteUser();
}
注解改进
声明公共切入点:
①、在 切面类 MyAspect.java 中新增一个切入点方法 myPointCut(),然后在这个方法上添加 @Pointcut 注解
事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。
这里我们以取钱的例子来讲解:比如你去ATM机取1000块钱,大体有两个步骤:第一步输入密码金额,银行卡扣掉1000元钱;第二步从ATM出1000元钱。这两个步骤必须是要么都执行要么都不执行。如果银行卡扣除了1000块但是ATM出钱失败的话,你将会损失1000元;如果银行卡扣钱失败但是ATM却出了1000块,那么银行将损失1000元。
如何保证这两个步骤不会出现一个出现异常了,而另一个执行成功呢?事务就是用来解决这样的问题。事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。 在企业级应用程序开发中,事务管理是必不可少的技术,用来确保数据的完整性和一致性。
事务的四个特性(ACID)
①、原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
②、一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
③、隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
④、持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。
PlatformTransactionManager 事务管理器
Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,Spring并不直接管理事务,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,也就是将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。
也就是说Spring事务管理的为不同的事务API提供一致的编程模型,具体的事务管理机制由对应各个平台去实现。
AccountDao 接口:
public interface AccountDao {
/**
* 汇款
* @param outer 汇款人
* @param money 汇款金额
*/
public void out(String outer,int money);
/**
* 收款
* @param inner 收款人
* @param money 收款金额
*/
public void in(String inner,int money);
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import com.my.dao.AccountDao;
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
/**
* 根据用户名减少账户金额
*/
@Override
public void out(String outer, int money) {
this.getJdbcTemplate().update("update account set money = money - ? where username = ?",money,outer);
}
/**
* 根据用户名增加账户金额
*/
@Override
public void in(String inner, int money) {
this.getJdbcTemplate().update("update account set money = money + ? where username = ?",money,inner);
}
}
public interface AccountService {
/**
* 转账
* @param outer 汇款人
* @param inner 收款人
* @param money 交易金额
*/
public void transfer(String outer,String inner,int money);
}
public class AccountServiceImpl implements AccountService{
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String outer, String inner, int money) {
accountDao.out(outer, money);
accountDao.in(inner, money);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<bean id="accountDao" class="com.my.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="accountService" class="com.my.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
</beans>
public class TransactionTest {
@Test
public void testNoTransaction(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService account = (AccountService) context.getBean("accountService");
//Tom 向 Marry 转账1000
account.transfer("Tom", "Marry", 1000);
}
}
编程式事务处理:所谓编程式事务指的是通过编码方式实现事务,允许用户在代码中精确定义事务的边界。即类似于JDBC编程实现事务管理。管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。
声明式事务处理:管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现。
编程式事务处理实现转账(TransactionTemplate )
我们在 Service 层注入 TransactionTemplate 模板,因为是用模板来管理事务,所以模板需要注入事务管理器 DataSourceTransactionManager 。而事务管理器说到底还是用底层的JDBC在管理,所以我们需要在事务管理器中注入 DataSource。这几个步骤分别如下:
AccountServiceImpl 接口:
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.my.dao.AccountDao;
import com.my.service.AccountService;
public class AccountServiceImpl implements AccountService{
private AccountDao accountDao;
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(final String outer,final String inner,final int money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
accountDao.out(outer, money);
//int i = 1/0;
accountDao.in(inner, money);
}
});
}
}
Spring 全局配置文件 applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<bean id="accountDao" class="com.my.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="accountService" class="com.my.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
<!-- 创建模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="txManager"></property>
</bean>
<!-- 配置事务管理器 ,管理器需要事务,事务从Connection获得,连接从连接池DataSource获得 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
底层的PlatformTransactionManager来进行事务管理
//定义一个某个框架平台的TransactionManager,如JDBC、Hibernate
DataSourceTransactionManager dataSourceTransactionManager =
new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(this.getJdbcTemplate().getDataSource()); // 设置数据源
DefaultTransactionDefinition transDef = new DefaultTransactionDefinition(); // 定义事务属性
transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED); // 设置传播行为属性
TransactionStatus status = dataSourceTransactionManager.getTransaction(transDef); // 获得事务状态
try {
// 数据库操作
accountDao.out(outer, money);
int i = 1/0;
accountDao.in(inner, money);
dataSourceTransactionManager.commit(status);// 提交
} catch (Exception e) {
dataSourceTransactionManager.rollback(status);// 回滚
}
声明式事务处理实现转账(基于AOP的 xml 配置)
Dao 层和 Service 层与我们最先开始的不用事务实现转账保持不变。主要是 applicationContext.xml 文件变化了。
我们在 applicationContext.xml 文件中配置 aop 自动生成代理,进行事务管理:
①、配置管理器
②、配置事务详情
③、配置 aop
<?xml version="1.0" encoding="UTF-8"?>
<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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<bean id="accountDao" class="com.my.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="accountService" class="com.my.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 1 事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2 事务详情(事务通知) , 在aop筛选基础上,比如对ABC三个确定使用什么样的事务。例如:AC读写、B只读 等
<tx:attributes> 用于配置事务详情(属性属性)
<tx:method name=""/> 详情具体配置
propagation 传播行为 , REQUIRED:必须;REQUIRES_NEW:必须是新的
isolation 隔离级别
-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/>
<tx:method name="*"/>//所有的方法
</tx:attributes>
</tx:advice>
<!-- 3 AOP编程,利用切入点表达式从目标类方法中 确定增强的连接器,从而获得切入点 -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.my.service..*.*(..))"/>//配置事物通知
<aop:pointcut expression="execution(* com.my.service..*.*(..))" id="serviceMethod">//配置切点
</aop:config>
</beans>
一、传播行为:当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。
Spring 定义了如下七中传播行为,这里以A业务和B业务之间如何传播事务为例说明:
①、PROPAGATION_REQUIRED :required , 必须。默认值,A如果有事务,B将使用该事务;如果A没有事务,B将创建一个新的事务。
②、PROPAGATION_SUPPORTS:supports ,支持。A如果有事务,B将使用该事务;如果A没有事务,B将以非事务执行。
③、PROPAGATION_MANDATORY:mandatory ,强制。A如果有事务,B将使用该事务;如果A没有事务,B将抛异常。
④、PROPAGATION_REQUIRES_NEW :requires_new,必须新的。如果A有事务,将A的事务挂起,B创建一个新的事务;如果A没有事务,B创建一个新的事务。
⑤、PROPAGATION_NOT_SUPPORTED :not_supported ,不支持。如果A有事务,将A的事务挂起,B将以非事务执行;如果A没有事务,B将以非事务执行。
⑥、PROPAGATION_NEVER :never,从不。如果A有事务,B将抛异常;如果A没有事务,B将以非事务执行。
⑦、PROPAGATION_NESTED :nested ,嵌套。A和B底层采用保存点机制,形成嵌套事务。
<!-- jdbc事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="edit*" propagation="REQUIRED" />
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="new*" propagation="REQUIRED" />
<tx:method name="set*" propagation="REQUIRED" />
<tx:method name="remove*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="change*" propagation="REQUIRED" />
<tx:method name="get*" propagation="REQUIRED" read-only="true" />
<tx:method name="find*" propagation="REQUIRED" read-only="true" />
<tx:method name="load*" propagation="REQUIRED" read-only="true" />
<tx:method name="*" propagation="REQUIRED" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- 配置事务切面 -->
<aop:config>
<!-- 配置切点 -->
<aop:pointcut id="serviceMethod" expression="execution(* com.java1234.service.*.*(..))" />
<!-- 配置事务通知 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod"/>
</aop:config>
二、隔离级别:定义了一个事务可能受其他并发事务影响的程度。
并发事务引起的问题:
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务。并发虽然是必须的,但可能会导致以下的问题。
①、脏读(Dirty reads)——脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
②、不可重复读(Nonrepeatable read)——不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
③、幻读(Phantom read)——幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。
注意:不可重复读重点是修改,而幻读重点是新增或删除。
在 Spring 事务管理中,为我们定义了如下的隔离级别:
①、ISOLATION_DEFAULT:使用后端数据库默认的隔离级别
②、ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
③、ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
④、ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
⑤、ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的
上面定义的隔离级别,在 Spring 的 TransactionDefinition.class 中也分别用常量 -1,0,1,2,4,8表示。
三、只读
这是事务的第三个特性,是否为只读事务。如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化。通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认为合适的优化措施。
四、事务超时
为了使应用程序很好地运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。
五、回滚规则
事务五边形的最后一个方面是一组规则,这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚(这一行为与EJB的回滚行为是一致的) 。但是你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。
声明式事务处理实现转账(基于AOP的 注解 配置)
分为如下两步:
①、在applicationContext.xml 配置事务管理器,将并事务管理器交予spring
②、在目标类或目标方法添加注解即可 @Transactional
首先在 applicationContext.xml 文件中配置如下:
<!-- 1 事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2 将管理器交予spring
* transaction-manager 配置事务管理器
* proxy-target-class
true : 底层强制使用cglib 代理
-->
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
其次在目标类或者方法添加注解@Transactional。如果在类上添加,则说明类中的所有方法都添加事务,如果在方法上添加,则只有该方法具有事务。
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.my.dao.AccountDao;
import com.my.service.AccountService;
@Transactional(propagation=Propagation.REQUIRED , isolation = Isolation.DEFAULT)
@Service("accountService")
public class AccountServiceImpl implements AccountService{
@Resource(name="accountDao")
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String outer, String inner, int money) {
accountDao.out(outer, money);
//int i = 1/0;
accountDao.in(inner, money);
}
}
spring对jdbc的支持
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="studentDao" class="com.java1234.dao.impl.StudentDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="studentService" class="com.java1234.service.impl.StudentServiceImpl">
<property name="studentDao" ref="studentDao"></property>
</bean>
</beans>
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db_spring
jdbc.username=root
jdbc.password=123456
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
public class StudentDaoImpl implements StudentDao{
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public int addStudent(Student student) {
String sql="insert into t_student values(null,?,?)";
Object []params=new Object[]{student.getName(),student.getAge()};
return jdbcTemplate.update(sql,params);
}
@Override
public int updateStudent(Student student) {
String sql="update t_student set name=?,age=? where id=?";
Object []params=new Object[]{student.getName(),student.getAge(),student.getId()};
return jdbcTemplate.update(sql,params);
}
@Override
public int deleteStudent(int id) {
String sql="delete from t_student where id=?";
Object []params=new Object[]{id};
return jdbcTemplate.update(sql,params);
}
@Override
public List<Student> findStudents() {
String sql="select * from t_student";
final List<Student> studentList=new ArrayList<Student>();
jdbcTemplate.query(sql, new RowCallbackHandler(){
@Override
public void processRow(ResultSet rs) throws SQLException {
Student student=new Student();
student.setId(rs.getInt("id"));
student.setName(rs.getString("name"));
student.setAge(rs.getInt("age"));
studentList.add(student);
}
});
return studentList;
}
}
import java.util.List;
public class StudentServiceImpl implements StudentService{
private StudentDao studentDao;
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}
@Override
public int addStudent(Student student) {
return studentDao.addStudent(student);
}
@Override
public int updateStudent(Student student) {
return studentDao.updateStudent(student);
}
@Override
public int deleteStudent(int id) {
return studentDao.deleteStudent(id);
}
@Override
public List<Student> findStudents() {
return studentDao.findStudents();
}
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class T {
private ApplicationContext ac;
@Before
public void setUp() throws Exception {
ac=new ClassPathXmlApplicationContext("beans.xml");
}
@Test
public void addStudent() {
StudentService studentService=(StudentService)ac.getBean("studentService");
int addNums=studentService.addStudent(new Student("王五", 1));
if(addNums==1){
System.out.println("添加成功");
}
}
@Test
public void updateStudent() {
StudentService studentService=(StudentService)ac.getBean("studentService");
int updateNums=studentService.updateStudent(new Student(8,"王五2", 2));
if(updateNums==1){
System.out.println("更新成功");
}
}
@Test
public void deleteStudent() {
StudentService studentService=(StudentService)ac.getBean("studentService");
int deleteNums=studentService.deleteStudent(8);
if(deleteNums==1){
System.out.println("删除成功");
}
}
@Test
public void findStudents() {
StudentService studentService=(StudentService)ac.getBean("studentService");
List<Student> studentList=studentService.findStudents();
for(Student student:studentList){
System.out.println(student);
}
}
}
支持命名参数变量
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>
<bean id="studentDao" class="com.java.dao.impl.StudentDaoImpl">
<property name="namedParameterJdbcTemplate" ref="namedParameterJdbcTemplate"></property>
</bean>
<bean id="studentService" class="com.java.service.impl.StudentServiceImpl">
<property name="studentDao" ref="studentDao"></property>
</bean>
</beans>
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
public class StudentDaoImpl implements StudentDao{
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setNamedParameterJdbcTemplate(
NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
}
@Override
public int addStudent(Student student) {
String sql="insert into t_student values(null,:name,:age)";
MapSqlParameterSource sps=new MapSqlParameterSource();
sps.addValue("name", student.getName());
sps.addValue("age", student.getAge());
return namedParameterJdbcTemplate.update(sql,sps);
}
@Override
public int updateStudent(Student student) {
String sql="update t_student set name=:name,age=:age where id=:id";
MapSqlParameterSource sps=new MapSqlParameterSource();
sps.addValue("name", student.getName());
sps.addValue("age", student.getAge());
sps.addValue("id", student.getId());
return namedParameterJdbcTemplate.update(sql,sps);
}
@Override
public int deleteStudent(int id) {
String sql="delete from t_student where id=:id";
MapSqlParameterSource sps=new MapSqlParameterSource();
sps.addValue("id", id);
return namedParameterJdbcTemplate.update(sql,sps);
}
@Override
public List<Student> findStudents() {
String sql="select * from t_student";
final List<Student> studentList=new ArrayList<Student>();
namedParameterJdbcTemplate.query(sql, new RowCallbackHandler(){
@Override
public void processRow(ResultSet rs) throws SQLException {
Student student=new Student();
student.setId(rs.getInt("id"));
student.setName(rs.getString("name"));
student.setAge(rs.getInt("age"));
studentList.add(student);
}
});
return studentList;
}
}
import java.util.List;
public class StudentServiceImpl implements StudentService{
private StudentDao studentDao;
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}
@Override
public int addStudent(Student student) {
return studentDao.addStudent(student);
}
@Override
public int updateStudent(Student student) {
return studentDao.updateStudent(student);
}
@Override
public int deleteStudent(int id) {
return studentDao.deleteStudent(id);
}
@Override
public List<Student> findStudents() {
return studentDao.findStudents();
}
}
spring整合hibernate
web,xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>S2SH</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 添加对spring的支持 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 定义Spring监听器,加载Spring -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 添加对struts2的支持 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Session延迟加载到页面 -->
<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>singleSession</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>openSessionInViewFilter</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>
</web-app>
struts.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<constant name="struts.action.extension" value="action" />
<package name="s2sh" namespace="/user" extends="struts-default">
<action name="user_*" method="{1}" class="com.java.action.UserAction">
<result name="success">/success.jsp</result>
<result name="error">/index.jsp</result>
</action>
</package>
</struts>
hibernate.cfg.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!--方言-->
<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- 显示sql语句 -->
<property name="show_sql">true</property>
<!-- 自动更新 -->
<property name="hbm2ddl.auto">update</property>
</session-factory>
</hibernate-configuration>
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<!-- 定义数据源 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="com.mysql.jdbc.Driver">
</property>
<property name="url"
value="jdbc:mysql://localhost:3306/test">
</property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</bean>
<!-- session工厂 -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="configLocation" value="classpath:hibernate.cfg.xml"/>
<!-- 自动扫描注解方式配置的hibernate类文件 -->
<property name="packagesToScan">
<list>
<value>com.java.entity</value> //包下的所有类都会扫描
</list>
</property>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 配置事务通知属性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 定义事务传播属性 -->
<tx:attributes>
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="edit*" propagation="REQUIRED" />
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="new*" propagation="REQUIRED" />
<tx:method name="set*" propagation="REQUIRED" />
<tx:method name="remove*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="change*" propagation="REQUIRED" />
<tx:method name="get*" propagation="REQUIRED" read-only="true" />
<tx:method name="find*" propagation="REQUIRED" read-only="true" />
<tx:method name="load*" propagation="REQUIRED" read-only="true" />
<tx:method name="*" propagation="REQUIRED" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- 配置事务切面 -->
<aop:config>
<aop:pointcut id="serviceOperation"
expression="execution(* com.java.service..*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation" />
</aop:config>
<!-- 自动加载构建bean -->
<context:component-scan base-package="com.java" /> //配置的类全部注解
</beans>
实体
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
@Entity
@Table(name="t_user")
public class User {
private Integer id;
private String userName;
private String password;
@Id
@GenericGenerator(name = "generator", strategy = "native")
@GeneratedValue(generator = "generator")
@Column(name = "id", length=11)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name = "userName", length = 20)
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Column(name = "password", length = 20)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
import java.io.Serializable;
import java.util.List;
/**
* 基础数据库操作类
*
*
*/
public interface BaseDao<T> {
/**
* 保存一个对象
*
* @param o
* @return
*/
public Serializable save(T o);
/**
* 删除一个对象
*
* @param o
*/
public void delete(T o);
/**
* 更新一个对象
*
* @param o
*/
public void update(T o);
/**
* 保存或更新对象
*
* @param o
*/
public void saveOrUpdate(T o);
/**
* 查询
*
* @param hql
* @return
*/
public List<T> find(String hql);
/**
* 查询集合
*
* @param hql
* @param param
* @return
*/
public List<T> find(String hql, Object[] param);
/**
* 查询集合
*
* @param hql
* @param param
* @return
*/
public List<T> find(String hql, List<Object> param);
/**
* 查询集合(带分页)
*
* @param hql
* @param param
* @param page
* 查询第几页
* @param rows
* 每页显示几条记录
* @return
*/
public List<T> find(String hql, Object[] param, Integer page, Integer rows);
/**
* 查询集合(带分页)
*
* @param hql
* @param param
* @param page
* @param rows
* @return
*/
public List<T> find(String hql, List<Object> param, Integer page, Integer rows);
/**
* 获得一个对象
*
* @param c
* 对象类型
* @param id
* @return Object
*/
public T get(Class<T> c, Serializable id);
/**
* 获得一个对象
*
* @param hql
* @param param
* @return Object
*/
public T get(String hql, Object[] param);
/**
* 获得一个对象
*
* @param hql
* @param param
* @return
*/
public T get(String hql, List<Object> param);
/**
* select count(*) from 类
*
* @param hql
* @return
*/
public Long count(String hql);
/**
* select count(*) from 类
*
* @param hql
* @param param
* @return
*/
public Long count(String hql, Object[] param);
/**
* select count(*) from 类
*
* @param hql
* @param param
* @return
*/
public Long count(String hql, List<Object> param);
/**
* 执行HQL语句
*
* @param hql
* @return 响应数目
*/
public Integer executeHql(String hql);
/**
* 执行HQL语句
*
* @param hql
* @param param
* @return 响应数目
*/
public Integer executeHql(String hql, Object[] param);
/**
* 执行HQL语句
*
* @param hql
* @param param
* @return
*/
public Integer executeHql(String hql, List<Object> param);
}
BaseDaOImpl.java
import java.io.Serializable;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository("baseDao")
@SuppressWarnings("all")
public class BaseDaOImpl<T> implements BaseDao<T> {
private SessionFactory sessionFactory;
public SessionFactory getSessionFactory() {
return sessionFactory;
}
@Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
private Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
public Serializable save(T o) {
return this.getCurrentSession().save(o);
}
public void delete(T o) {
this.getCurrentSession().delete(o);
}
public void update(T o) {
this.getCurrentSession().update(o);
}
public void saveOrUpdate(T o) {
this.getCurrentSession().saveOrUpdate(o);
}
public List<T> find(String hql) {
return this.getCurrentSession().createQuery(hql).list();
}
public List<T> find(String hql, Object[] param) {
Query q = this.getCurrentSession().createQuery(hql);
if (param != null && param.length > 0) {
for (int i = 0; i < param.length; i++) {
q.setParameter(i, param[i]);
}
}
return q.list();
}
public List<T> find(String hql, List<Object> param) {
Query q = this.getCurrentSession().createQuery(hql);
if (param != null && param.size() > 0) {
for (int i = 0; i < param.size(); i++) {
q.setParameter(i, param.get(i));
}
}
return q.list();
}
public List<T> find(String hql, Object[] param, Integer page, Integer rows) {
if (page == null || page < 1) {
page = 1;
}
if (rows == null || rows < 1) {
rows = 10;
}
Query q = this.getCurrentSession().createQuery(hql);
if (param != null && param.length > 0) {
for (int i = 0; i < param.length; i++) {
q.setParameter(i, param[i]);
}
}
return q.setFirstResult((page - 1) * rows).setMaxResults(rows).list();
}
public List<T> find(String hql, List<Object> param, Integer page, Integer rows) {
if (page == null || page < 1) {
page = 1;
}
if (rows == null || rows < 1) {
rows = 10;
}
Query q = this.getCurrentSession().createQuery(hql);
if (param != null && param.size() > 0) {
for (int i = 0; i < param.size(); i++) {
q.setParameter(i, param.get(i));
}
}
return q.setFirstResult((page - 1) * rows).setMaxResults(rows).list();
}
public T get(Class<T> c, Serializable id) {
return (T) this.getCurrentSession().get(c, id);
}
public T get(String hql, Object[] param) {
List<T> l = this.find(hql, param);
if (l != null && l.size() > 0) {
return l.get(0);
} else {
return null;
}
}
public T get(String hql, List<Object> param) {
List<T> l = this.find(hql, param);
if (l != null && l.size() > 0) {
return l.get(0);
} else {
return null;
}
}
public Long count(String hql) {
return (Long) this.getCurrentSession().createQuery(hql).uniqueResult();
}
public Long count(String hql, Object[] param) {
Query q = this.getCurrentSession().createQuery(hql);
if (param != null && param.length > 0) {
for (int i = 0; i < param.length; i++) {
q.setParameter(i, param[i]);
}
}
return (Long) q.uniqueResult();
}
public Long count(String hql, List<Object> param) {
Query q = this.getCurrentSession().createQuery(hql);
if (param != null && param.size() > 0) {
for (int i = 0; i < param.size(); i++) {
q.setParameter(i, param.get(i));
}
}
return (Long) q.uniqueResult();
}
public Integer executeHql(String hql) {
return this.getCurrentSession().createQuery(hql).executeUpdate();
}
public Integer executeHql(String hql, Object[] param) {
Query q = this.getCurrentSession().createQuery(hql);
if (param != null && param.length > 0) {
for (int i = 0; i < param.length; i++) {
q.setParameter(i, param[i]);
}
}
return q.executeUpdate();
}
public Integer executeHql(String hql, List<Object> param) {
Query q = this.getCurrentSession().createQuery(hql);
if (param != null && param.size() > 0) {
for (int i = 0; i < param.size(); i++) {
q.setParameter(i, param.get(i));
}
}
return q.executeUpdate();
}
}
public interface UserService {
public void saveUser(User user);
public void updateUser(User user);
public User findUserById(int id);
public void deleteUser(User user);
public List<User> findAllList();
public User findUserByNameAndPassword(User user);
}
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.java.dao.BaseDao;
import com.java.entity.User;
import com.java.service.UserService;
@Service("userService")
public class UserServiceImpl implements UserService{
@Resource
private BaseDao<User> baseDao;
@Override
public void saveUser(User user) {
// TODO Auto-generated method stub
baseDao.save(user);
}
@Override
public void updateUser(User user) {
// TODO Auto-generated method stub
baseDao.update(user);
}
@Override
public User findUserById(int id) {
return baseDao.get(User.class, id);
}
@Override
public void deleteUser(User user) {
baseDao.delete(user);
}
@Override
public List<User> findAllList() {
return baseDao.find("from User");
}
@Override
public User findUserByNameAndPassword(User user) {
return baseDao.get("from User u where u.userName=? and u.password=?", new Object[]{user.getUserName(),user.getPassword()});
}
}
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.springframework.stereotype.Controller;
import com.opensymphony.xwork2.ActionSupport;
@Controller
public class UserAction extends ActionSupport implements ServletRequestAware{
/**
*
*/
private static final long serialVersionUID = 1L;
private HttpServletRequest request;
@Resource
private UserService userService;
private User user;
private String error;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public String login()throws Exception{
HttpSession session=request.getSession();
User currentUser=userService.findUserByNameAndPassword(user);
if(currentUser!=null){
session.setAttribute("currentUser", currentUser);
return SUCCESS;
}else{
error="用后名或者密码错误!";
return ERROR;
}
}
@Override
public void setServletRequest(HttpServletRequest request) {
// TODO Auto-generated method stub
this.request=request;
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/user/user_login.action" method="post">
userName:<input type="text" name="user.userName" value="${user.userName }"/><br/>
password:<input type="password" name="user.password" value="${user.password }"><br/>
<input type="submit" value="login"/><font color="red">${error }</font>
</form>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
欢迎:${currentUser.userName }
</body>
</html>
spring框架搭建
web,xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<display-name>prodatashow</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/spring*.xml</param-value>
</context-param>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet>
<servlet-name>AuthImg</servlet-name>
<servlet-class>com.platform.utils.AuthImg</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AuthImg</servlet-name>
<url-pattern>/authImage</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>/cusviews/index</welcome-file>
</welcome-file-list>
<error-page>
<error-code>404</error-code>
<location>/404Error</location>
</error-page>
</web-app>
spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<!-- 自动扫描 -->
<context:component-scan base-package="com.**.controller">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
<!-- <mvc:annotation-driven /> -->
<!-- 静态资源 开始 -->
<mvc:resources location="/404Error.html" mapping="/404Error.html"/>
<mvc:resources location="/view/**" mapping="/view/**"/>
<mvc:resources location="/application/**" mapping="/application/**"/>
<mvc:resources mapping="/platform/**" location="/platform/,classpath:/platform/" />
<mvc:resources mapping="/favicon.ico" location="/favicon.ico,classpath:/favicon.ico" />
<!-- 静态资源 结束 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<mvc:exclude-mapping path="/" />
<mvc:exclude-mapping path="/login" />
<mvc:exclude-mapping path="/loginCheck" />
<mvc:exclude-mapping path="/view/**" />
<mvc:exclude-mapping path="/platform/**" />
<mvc:exclude-mapping path="/application/**" />
<bean class="com.interceptor.CusViewsInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<!--避免IE执行AJAX时,返回JSON出现下载文件 -->
<bean id="mappingJacksonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" value="#{jsonUtils.mapper}"/> <!-- 用于日期输出格式转换 -->
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>*; charset=UTF-8</value>
</list>
</property>
</bean>
<!-- 自定义拦截处理 类-->
<bean id="cntenAccessInterceptor" class="com.platform.framework.web.interceptor.implAccessInterceptor"></bean>
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<ref bean="cntenAccessInterceptor" />
</list>
</property>
</bean>
<!-- 启动SpringMVC的注解功能,完成请求和注解POJO的映射 -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="mappingJacksonHttpMessageConverter" /> <!-- JSON转换器 -->
</list>
</property>
</bean>
<!-- jsp 视图解析-->
<bean id="jspResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 这里的配置我的理解是自动给后面action的方法return的字符串加上前缀和后缀,变成一个 可用的url地址 -->
<property name="contentType" value="text/html;charset=UTF-8"/>
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
<property name="order" value="1"/>
</bean>
<!-- freemarker的配置 -->
<bean id="freemarkerConfigurer" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPaths">
<list>
<value>/view</value>
</list>
</property>
<property name="defaultEncoding" value="UTF-8" />
<property name="freemarkerVariables">
<map>
<entry key="downSelector" value-ref="downSelectorDirective"/>
<entry key="checkBox" value-ref="checkBoxDirective"/>
<entry key="redioBox" value-ref="redioBoxDirective"/>
<entry key="popSelector" value-ref="popSelectorDirective"/>
<entry key="uploadPlugin" value-ref="uploadPluginDirective"/>
</map>
</property>
<property name="freemarkerSettings">
<props>
<prop key="template_update_delay">10</prop>
<prop key="locale">zh_CN</prop>
<prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop>
<prop key="date_format">yyyy-MM-dd</prop>
<prop key="number_format">#.##</prop>
</props>
</property>
</bean>
<bean id="freemarkerResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true" />
<property name="suffix" value=".html" />
<property name="order" value="0"/>
<property name="contentType">
<value>text/html;charset=UTF-8</value>
</property>
<property name="viewClass">
<value>com.platform.framework.web.viewFreeMarkerView</value>
</property>
<property name="requestContextAttribute" value="rc"></property>
</bean>
<!-- 配置文件上传,如果没有使用文件上传可以不用配置,当然如果不配,那么配置文件中也不必引入上传组件包 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 默认编码 -->
<property name="defaultEncoding" value="UTF-8" />
<!-- 文件大小最大值 -->
<property name="maxUploadSize" value="10485760000" />
<!-- 内存中的最大值 -->
<property name="maxInMemorySize" value="40960" />
</bean>
</beans>
spring-common.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<!-- 引入配置文件 -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<!-- <property name="location" value="classpath*:config/*.properties" /> -->
<property name="locations">
<list>
<value>classpath:config/cache.properties</value>
<value>classpath:config/jdbc.properties</value>
<value>classpath:config/upload.properties</value>
</list>
</property>
</bean>
<!-- 日期处理时间类 -->
<bean id="jsonUtils" class="com.platform.utils.JsonUtil" lazy-init="false"/>
<!-- bean 管理器 -->
<bean id="springContextHolder" class="com.platform.framework.context.SpringContextHolder" lazy-init="false"/>
<!-- http session 处理类 -->
<bean id="httpSessionService" class="com.platform.framework.session.impl.HttpSessionService" lazy-init="false"/>
<!-- 缓存 处理类 -->
<bean id="ehCacheManagerImpl" class="com.platform.framework.cache.impl.EhCacheManagerImpl" lazy-init="false"/>
<bean id="redisCacheManagerImpl" class="com.platform.framework.cache.impl.RedisCacheManagerImpl" lazy-init="false"/>
<bean id="cacheManager" class="net.sf.ehcache.CacheManager" lazy-init="false"/>
<!-- spring和MyBatis整合,不需要mybatis的配置映射文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dynamicDataSource" />
<property name="databaseIdProvider" ref="databaseIdProvider" />
<property name="configLocation" value="classpath:config/mybatis-config.xml"></property>
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations" value="classpath*:com/cnten/**/mapping/*.xml"></property>
</bean>
<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.**.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<!-- 扫描所有Servcie类 -->
<context:component-scan base-package="com.**.service" />
<context:component-scan base-package="com.platform.common.job" />
<!-- 在包com.common.aop及其子包下面自动扫描通过注解配置的组件 -->
<context:component-scan base-package="com.platform.framework"/>
<!-- 激活自动代理功能 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--
properties, settings, typeAliases, typeHandlers,
objectFactory, objectWrapperFactory, reflectorFactory,
plugins, environments, databaseIdProvider, mappers
-->
<configuration>
<!-- 设置管理 -->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 打印查询语句 -->
<setting name="logImpl" value="com.commonLogger" />
<setting name="callSettersOnNulls" value="true"/>
<setting name="jdbcTypeForNull" value="NULL"/>
</settings>
<!-- map 驼峰处理 -->
<objectWrapperFactory type="com.platform.framework.mybatis.map.MapWrapperFactory"/>
<!-- 插件管理 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
</configuration>
import org.apache.ibatis.logging.Log;
public class commonLogger implements Log {
public commonLogger(String clazz) {
}
public boolean isDebugEnabled() {
return true;
}
public boolean isTraceEnabled() {
return false;
}
public void error(String s, Throwable e) {
System.err.println(s);
e.printStackTrace(System.err);
}
public void error(String s) {
System.err.println(s);
}
public void debug(String s) {
System.out.println(s);
}
public void trace(String s) {
System.out.println(s);
}
public void warn(String s) {
System.out.println(s);
}
}
MapWrapperFactory
import java.util.Map;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.wrapper.MapWrapper;
public class commonLoggerMapWrapper
extends MapWrapper
{
public commonLoggerMapWrapper(MetaObject metaObject, Map<String, Object> map)
{
super(metaObject, map);
}
public String findProperty(String name, boolean useCamelCaseMapping)
{
if ((useCamelCaseMapping) && (
((name.charAt(0) >= 'A') && (name.charAt(0) <= 'Z')) ||
(name.indexOf("_") >= 0))) {
return underlineToCamelhump(name);
}
return name;
}
public String underlineToCamelhump(String inputString)
{
StringBuilder sb = new StringBuilder();
boolean nextUpperCase = false;
for (int i = 0; i < inputString.length(); i++)
{
char c = inputString.charAt(i);
if (c == '_')
{
if (sb.length() > 0) {
nextUpperCase = true;
}
}
else if (nextUpperCase)
{
sb.append(Character.toUpperCase(c));
nextUpperCase = false;
}
else
{
sb.append(Character.toLowerCase(c));
}
}
return sb.toString();
}
}
import java.util.Map;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.wrapper.ObjectWrapper;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
public class MapWrapperFactory
implements ObjectWrapperFactory
{
public boolean hasWrapperFor(Object object)
{
return (object != null) && ((object instanceof Map));
}
public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object)
{
return new commonLoggerMapWrapper(metaObject, (Map)object);
}
}
upload.properties
#============================================================upload config properties===============================================================
## 上传文件路径路径 (linux)
#uploadDir=/usr/local/tomcat/upload/
## 上传文件路径路径 (window)
uploadDir=D:\\dev_space\\workspaces\\newPlatform\\upload
CommonFreeMarkerView
package com.platform.framework.web.view;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.servlet.view.freemarker.FreeMarkerView;
import com.platform.consts.Constant;
import com.platform.consts.LoginConstant;
/**
* 重写freemark视图解析类
* @author pzh
*/
public class CommonFreeMarkerView extends FreeMarkerView {
@SuppressWarnings("unchecked")
@Override
protected void exposeHelpers(Map<String, Object> model,
HttpServletRequest request) throws Exception {
model.put("context_path", request.getContextPath()); //上下文
String act = request.getParameter("act");
String menuCode = request.getParameter("menuCode");
model.put("pageAct", act);
if(Constant.READ.equals(act)) {
model.put("edited", "disabled"); //禁止操作
}else{
model.put("edited", "");
}
//处理页面按钮的隐藏|显示
List<Map<String, Object>> authActs = (List<Map<String, Object>>) request.getSession().getAttribute(LoginConstant.LOGIN_USER_AUTH_PERMISSION);
if(authActs !=null && authActs.size()>0) {
for (Map<String, Object> map : authActs) {
String actNo = map.get("actNo").toString();
if(menuCode != null && menuCode.equals(map.get("menuCode"))) {
if(Integer.valueOf(actNo) == 1) {
model.put(String.valueOf(map.get("actCode")), "");
}else{
model.put(String.valueOf(map.get("actCode")), map.get("actCode"));
}
}
}
}
super.exposeHelpers(model, request);
}
}
CommonAccessInterceptor
package com.platform.framework.web.interceptor.impl;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import com.platform.framework.context.ThreadContextHolder;
import com.platform.framework.web.interceptor.ILocalInterceptor;
public class CommonAccessInterceptor extends HandlerInterceptorAdapter{
@SuppressWarnings("unused")
private ILocalInterceptor localInterceptor; //本地自定义拦截器, 由业务代码实现
/**
* 拦截 spring mvc的所有请求
* 并设置request、response到线程变量中
* 实现request any where
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//记录请求对象
ThreadContextHolder.setHttpRequest(request);
ThreadContextHolder.setHttpResponse(response);
//非法字符过滤处理
Pattern pattern = Pattern.compile("'|<|>");
for(String[] paramValues : request.getParameterMap().values()){
for(String paramValue : paramValues){
if(pattern.matcher(paramValue).find()){
return false;
}
}
}
//父类只处理通用部分,本地化业务由具体业务实现
// localInterceptor = SpringContextHolder.getBean("localInterceptor");
// if(null != localInterceptor)
// localInterceptor.preHandle(request, response, handler);
return super.preHandle(request, response, handler);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
super.afterCompletion(request, response, handler, ex);
ThreadContextHolder.clearThreadValues();
}
}
CusViewsInterceptor
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class CusViewsInterceptor extends HandlerInterceptorAdapter{
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
super.postHandle(request, response, handler, modelAndView);
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return super.preHandle(request, response, handler);
}
}
log4j.properties
#定义LOG输出级别 ERROR、WARN、INFO、DEBUG
#log4j.rootLogger=INFO,Console,File
log4j.rootLogger=INFO,Console
#定义日志输出目的地为控制台
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.Target=System.out
#可以灵活地指定日志输出格式,下面一行是指定具体的格式
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=[%c] - %m%n
#文件大小到达指定尺寸的时候产生一个新的文件
log4j.appender.File=org.apache.log4j.RollingFileAppender
#指定输出目录
log4j.appender.File.File=D:/logs/ssm.log
#定义文件最大大小
log4j.appender.File.MaxFileSize=10MB
#输出所有日志,如果换成DEBUG表示输出DEBUG以上级别日志
log4j.appender.File.Threshold=ALL
log4j.appender.File.layout=org.apache.log4j.PatternLayout
log4j.appender.File.layout.ConversionPattern=[%p] [%d{yyyy-MM-dd HH\:mm\:ss}][%c]%m%n
#控制框架日志
log4j.logger.org.springframework=ERROR
log4j.logger.org.mybatis=ERROR
log4j.logger.org.apache=ERROR
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private final static Logger log = LoggerFactory.getLogger(AreaController.class);
import org.apache.log4j.Logger;
import java.text.MessageFormat;
private static Logger logger = Logger.getLogger(WorkManager.class);
logger.info(MessageFormat.format("====sys worker {0} syn date start time:{1}====", ruleCode,
DateUtil.formatTime(now)));