• 1. Spring企业开发广西柳州创景软件人才实训中心 www.cjitpx.com
  • 2. Spring是什么Spring是一个开源的控制反转(Inversion of Control ,IoC)和面向切面(AOP)的容器框架.它的主要目的是简化企业开发.
  • 3. IOC 控制反转 public class PersonService {//服务层 private PersonDao personDao = new PersonDaoBean(); public void save(Person person){//保存一个对象 personDao.save(person); } } PersonDaoBean 是在应用内部创建及维护的(在PersonService 类中new出来的)。所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转。
  • 4. 依赖注入(Dependency Injection)当我们把依赖对象交给外部容器负责创建,那么PersonService 类可以改成如下: public class PersonService { private PersonDao personDao ; //通过构造器参数,让容器把创建好的依赖对象注入进PersonService,当然也可以使用setter方法进行注入。 public PersonService(PersonDao personDao){ this.personDao=personDao; } public void save(Person person){ personDao.save(person); } } 所谓依赖注入就是指:在运行期,由外部容器动态地将依赖对象注入到组件中。
  • 5. 为何要使用Spring在项目中引入spring立即可以带来下面的好处 降低组件之间的耦合度,实现软件各层之间的解耦。 可以使用容器提供的众多服务,如:事务管理服务、消息服务等等。当我们使用容器管理事务时,开发人员就不再需要手工控制事务.也不需处理复杂的事务传播。 容器提供单例模式支持,开发人员不再需要自己编写实现代码。 容器提供了AOP技术,利用它很容易实现如权限拦截、运行期监控等功能。 容器提供的众多辅作类,使用这些类能够加快应用的开发,如: JdbcTemplate、 HibernateTemplate。 Spring对于主流的应用框架提供了集成支持,如:集成Hibernate、JPA、Struts等,这样更便于应用的开发。ControllerServiceDAO
  • 6. 使用Spring的好处 当使用spring时,我们可以使用容器提供的众多服务
  • 7. 如果使用Spring, 我们就不再需要手工控制事务public void savePerson(){ People people = new People(); people.setCardNo("452225197811246239"); people.setName("李四"); try { session.save(people); transaction.commit(); } catch (HibernateException e) { transaction.rollback(); e.printStackTrace(); } }使用Hibernate框架需要手工提交事务
  • 8. 另外,如果使用spring, 我们也不需要处理复杂的事务传播行为public void payment(){ Bean1.update();//更新金额 Bean2.save();//记录操作日志 } 如果我们不使用Spring,针对下面这两种业务需求,我们该如何做? 第1种可能的业务需求:要求Bean1.update()和Bean2.save()在同一个事务中执行。 第2种可能的业务需求:要求不管Bean1.update() 的事务是否成功,都需要记录操作日志。 public class Bean1 { public void update(){//注意:下面省略了一些代码 Connection conn = null; conn.setAutoCommit(false); Statement.executeUpdate(“update account set amount=? where id=?"); } } public class Bean2 { public void save(){//注意:下面省略了一些代码 Connection conn = null; conn.setAutoCommit(false); Statement.executeUpdate(“insert into Log (content) values (?)"); } }
  • 9. 使用Spring,不再需要我们处理复杂的事务传播行为使用Spring,我们只需要通过声明式的事务属性配置就可以轻松地实现这两种业务需求 1.要求Bean1.update()和Bean2.save()的在同一个事务中执行 2.要求不管Bean1.update() 的事务是否成功,都需要记录日志。 @Transactional(propagation=Propagation.Required) public void payment(){ Bean1.update();//更新金额 Bean2.save();//记录日志 } public class Bean1 { @Transactional(propagation=Propagation.Required) public void update(){ executeUpdate(“update account set amount=? where id=?"); } } public class Bean2 { @Transactional(propagation=Propagation.RequiresNew) public void save(){ executeUpdate(“insert into Log (content) values (?)"); } }
  • 10. 轻量级与重量级概念的划分经常会有同学问到spring属于轻量级框架,还是重量框架?其实划分一个应用是否属于轻量级还是重量级,主要看它使用了多少服务.使用的服务越多,容器要为普通java对象做的工作就越多,必然会影响到应用的发布时间或者是运行性能. 对于spring容器,它提供了很多服务,但这些服务并不是默认为应用打开的,应用需要某种服务,还需要指明使用该服务,如果应用使用的服务很少,如:只使用了spring核心服务,那么我们可以认为此时应用属于轻量级的,如果应用使用了spring提供的大部分服务,这时应用就属于重量级。目前EJB容器就因为它默认为应用提供了EJB规范中所有的功能,所以它属于重量级。
  • 11. 使用Spring需要的jar到http://www.springsource.org/download下载spring,然后进行解压缩,在解压目录中找到下面jar文件,拷贝到类路径下 dist\spring.jar lib\jakarta-commons\commons-logging.jar 如果使用了切面编程(AOP),还需要下列jar文件 lib/aspectj/aspectjweaver.jar和aspectjrt.jar lib/cglib/cglib-nodep-2.1_3.jar 如果使用了JSR-250中的注解,如@Resource/@PostConstruct/@PreDestroy,还需要下列jar文件 lib\j2ee\common-annotations.jar 本次课程使用的Spring版本是2.5
  • 12. 使用myeclipse添加Spring应用第一步:创建web工程(其实普通j2se工程也可以)
  • 13. 使用myeclipse添加Spring应用第二步:在web工程中添加Spring框架支持
  • 14. 使用myeclipse添加Spring应用这里只是简单应用,没有涉及到和其它框架的集成,因此选择前面两个就可以了。
  • 15. 使用myeclipse添加Spring应用这步的目的是生成Spring配置文件applicationContext.xml文件位置是src
  • 16. 使用myeclipse添加Spring应用框架添加完成之后的效果
  • 17. 使用myeclipse添加Spring应用public class UserInfo { private Integer id; private String name; private String address; private Integer age; //set and get method…… }第三步:创建一个实体类;并生成属性对应的set/get方法
  • 18. 使用myeclipse添加Spring应用第四步:根据左边的实体类属性名称和数据类型在Spring配置文件中配置一个bean;这个bean的id值是userInfoBean这个id属性名称对应的值在applicationContext.xml必须是唯一;不能存在同名;在应用程序中将根据id名称来获取这个bean
  • 19. 使用myeclipse添加Spring应用//编写测试类,从Spring容器中获取userInfoBean package cn.com.chongking; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class BeanUtil { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserInfo userInfo = (UserInfo)context.getBean(“normalBean"); System.out.println(userInfo.getName()+":"+userInfo.getAddress()); } }第五步:编写测试类;从spring容器中根据bean的id获取bean对象userInfoBean是applicationContext.xml配置文件中的bean id属性值applicationContext.xml
  • 20. 使用eclipse添加Spring应用使用eclipse开发Spring应用需要自己添加使用到的jar文件和spring配置文件(applicationContext.xml)其它的配置和测试跟使用myeclipse是一样的。在java体系中绝大部分的框架应用所涉及到的引用基本上都是jar文件和配置文件。 Jar文件和配置文件有多种渠道可以获取;可以从官方网站上下载;可以使用别人现有的;可以通过其它渠道获取。
  • 21. spring的配置文件模版 ..... 该配置模版可以从spring的参考手册或spring的例子中得到。配置文件的取名可以任意,文件可以存放在任何目录下,但考虑到通用性,一般放在类路径下。
  • 22. 编写spring配置文件时,不能出现帮助信息由于spring的schema文件位于网络上,如果机器不能连接到网络,那么在编写配置信息时候就无法出现提示信息,解决方法有两种: 1。让机器上网,eclipse会自动从网络上下载schema文件并缓存在硬盘上。 2。手动添加schema文件,方法如下: windwos->preferences->myeclipse->files and editors->xml->xmlcatalog 点"add",在出现的窗口中的Key Type中选择URI,在location中选"File system",然后在spring解压目录的dist/resources目录中选择spring-beans-2.5.xsd,回到设置窗口的时候不要急着关闭窗口,应把窗口中的Key Type改为Schema location,Key改为http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
  • 23. 实例化spring容器实例化Spring容器常用的两种方式: 方法一: 在类路径下寻找配置文件来实例化容器 ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"beans.xml"}); 方法二: 在文件系统路径下寻找配置文件来实例化容器 ApplicationContext ctx = new FileSystemXmlApplicationContext(new String[]{“d:\\beans.xml“}); Spring的配置文件可以指定多个,可以通过String数组传入。
  • 24. 从spring容器中得到bean当spring容器启动后,因为spring容器可以管理bean对象的创建,销毁等生命周期,所以我们只需从容器直接获取Bean对象就行,而不用编写一句代码来创建bean对象。从容器获取bean对象的代码如下: ApplicationContext ctx = new ClassPathXmlApplicationContext(“beans.xml”); OrderService service = (OrderService)ctx.getBean("personService");
  • 25. 使用dom4j读取spring配置文件public class ClassPathXmlApplicationContext { private List beanDefines = new ArrayList(); public ApplicationContext(String filename){ init(filename); } private void init(String filename){ SAXReader saxReader = new SAXReader(); Document document=null; try{ URL xmlpath = this.getClass().getClassLoader().getResource(filename); document = saxReader.read(xmlpath); Map nsMap = new HashMap(); nsMap.put("ns","http://www.springframework.org/schema/beans");//加入命名空间 XPath xsub = document.createXPath("//ns:beans/ns:bean");//创建beans/bean查询路径 xsub.setNamespaceURIs(nsMap);//设置命名空间 List beans = xsub.selectNodes(document);//获取文档下所有bean节点 for(Element element: beans){ String id = element.attributeValue("id");//获取id属性值 String clazz = element.attributeValue("class"); //获取class属性值 BeanDefinition beanDefine = new BeanDefinition(id, clazz); beanDefines.add(beanDefine); } }catch(Exception e){ e.printStackTrace(); } } }
  • 26. 三种实例化bean的方式-使用类构造器实例化//实体类设计 public class UserInfo { private Integer id; private String name; private String address; private Integer age; //构造子 public UserInfo(){} //构造子 public UserInfo(Integer id, String name, String address,Integer age){ this.id = id; this.name = name; this.address = address; this.age = age; } //set and get method… }applicationContext.xml配置 //从Spring容器获取Bean测试代码 public void getUserInfo(){ UserInfo userInfo = (UserInfo)context.getBean("normalBean"); System.out.println(userInfo.getId()+" : "+userInfo.getName()+":"+userInfo.getAddress()); }
  • 27. 三种实例化bean的方式-使用静态工厂方法实例化//工厂类设计 public class BeanFactory { public static UserInfo factoryDao(){ return new UserInfo(1,"王五","南宁",21); } }applicationContext.xml配置 public void staticFactoryBean(){//测试代码 UserInfo userInfo = (UserInfo)context.getBean("staticFactoryBean"); System.out.println(userInfo.getId()+" : "+userInfo.getName()+":"+userInfo.getAddress()); }//实体类设计 public class UserInfo { private Integer id; private String name; private String address; private Integer age; //构造子 public UserInfo(){} //构造子 public UserInfo(Integer id, String name, String address,Integer age){ this.id = id; this.name = name; this.address = address; this.age = age; } //set and get method… }
  • 28. 三种实例化bean的方式-使用实例工厂方法实例化//工厂类设计 public class BeanFactory { public UserInfo createUserInfo(){ return new UserInfo(1,"王五","南宁",21); } }applicationContext.xml配置 //使用实例工厂方法实例化测试 public void instanceFactoryBean(){ UserInfo userInfo = (UserInfo)context.getBean("instanceFactoryBean"); System.out.println(userInfo.getId()+" : "+userInfo.getName()+":"+userInfo.getAddress()); }
  • 29. Bean的作用域(scope属性).singleton 在每个Spring IoC容器中一个bean定义只有一个对象实例。默认情况下会在容器启动时初始化bean,但我们可以指定Bean节点的lazy-init=“true”来延迟初始化bean,这时候,只有第一次获取bean会才初始化bean。如: 如果想对所有bean都应用延迟初始化,可以在根节点beans设置default-lazy-init=“true“,如下: 30. 指定Bean的初始化方法和销毁方法指定Bean的初始化方法和销毁方法 import org.junit.*; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class BeanUtil { AbstractApplicationContext context = null;//Spring容器对象(注意:不是ApplicationContext) @Before public void before(){ System.out.println("before..."); context = new ClassPathXmlApplicationContext("applicationContext.xml"); } @Test public void getEmployeeBean(){ //Employee类中有两个方法:init();和close() Employee employee = (Employee)context.getBean("empBean"); System.out.println(employee.getName()); } @After public void after(){ context.close();//需要显示调用 } }
  • 31. 注入依赖对象-基本类型对象注入 public class Employee {//实体类设计 private Integer id; private String name; private String address; public Employee(){ System.out.println("不带参数的构造方法...."); } public Employee(Integer id,String name,String address){ System.out.println("带参数构造函数.........."); this.id = id; this.name = name; this.address = address; } //set and get method.........
  • 32. 注入依赖对象-构造器注入applicationContext.xml配置 实体类构造方法 public Employee(Integer id,String name,String address){ System.out.println("带参数构造函数.........."); this.id = id; this.name = name; this.address = address; } @Test public void getEmployeeBean(){//测试方法 Employee employee = (Employee)context.getBean("empBean"); System.out.println(employee.getId()+" : "+employee.getName()+" : "+employee.getAddress()); }
  • 33. 注入依赖对象-注入其它Bean-1applicationContext.xml配置 public class Cpu { public void hardwareInfo(){ System.out.println("AMD 速龙"); } }@Test public void getComputer(){//测试类 Computer computer = (Computer)context.getBean("computerBean"); computer.testCput(); }public class Computer { private Cpu cpu; //set方法是必须的(set注入) public void setCpu(Cpu cpu) { this.cpu = cpu; } public Cpu getCpu() { return cpu; } public void testCput() { cpu.hardwareInfo(); } }
  • 34. 注入依赖对象-注入其它Bean-2applicationContext.xml配置 public class Cpu { public void hardwareInfo(){ System.out.println("AMD 速龙"); } }@Test public void getComputer(){//测试类 Computer computer = (Computer)context.getBean("computerBean"); computer.testCput(); }public class Computer { private Cpu cpu; //set方法是必须的(set注入) public void setCpu(Cpu cpu) { this.cpu = cpu; } public Cpu getCpu() { return cpu; } public void testCput() { cpu.hardwareInfo(); } }使用内部bean,但该bean不能被其他bean使用
  • 35. 练习-通过Bean注入模拟计算机组装//接口设计 public interface USB { //USB接口信息 public void info(String content); } public interface Iprint { //打印机接口信息 public void info(String content); }public class Memory {//内存类 public void info(String config) { System.out.println("内存配置:"+config); } } public class Computer {//计算机类 private USB usb; private Iprint print; private Memory memory; public void showHardWareInfo(){ usb.info("移动硬盘"); print.info("彩色打印机"); memory.info("金士顿内存条"); } //省略了set、get方法 }applicationContext.xml配置 @Test public void getComputer(){//模拟计算机组装测试方法 Computer computer = (Computer)context.getBean("computerBean"); computer.showHardWareInfo(); }//接口实现类 public class UDisk implements USB { public void info(String config) { System.out.println("usb接口配置:"+config); } } public class ColorPrint implements Iprint { public void info(String config) { System.out.println("彩色打印机:"+config); } }
  • 36. 集合类型的装配 cjkj set cjkj @Test public void getOrder(){//测试类 Order order = (Order)context.getBean("order"); List lists = order.getLists(); for (String s : lists) { System.out.println(s); } }public class Order { private Set sets = new HashSet(); private List lists = new ArrayList(); private Properties properties = new Properties(); private Map maps = new HashMap(); ....//这里省略属性的getter和setter方法 }
  • 37. 依赖注入使用构造器注入 使用属性setter方法注入 使用Field注入(用于注解方式) 注入依赖对象可以采用手工装配或自动装配,在实际应用中建议使用手工装配,因为自动装配会产生未知情况,开发人员无法预见最终的装配结果。 1.手工装配依赖对象 2.自动装配依赖对象
  • 38. 依赖注入--手工装配手工装配依赖对象,在这种方式中又有两种编程方式 1. 在xml配置文件中,通过在bean节点下配置,如 //构造器注入(带参构造函数第1个值) //构造器注入(带参构造函数第2个值) //构造器注入(带参构造函数第3个值) //属性setter方法注入 2. 在java代码中使用@Autowired或@Resource注解方式进行装配。但我们需要在xml配置文件中配置以下信息: 这个配置隐式注册了多个对注释进行解析处理的处理器:AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor 注: @Resource注解在spring安装目录的lib\j2ee\common-annotations.jar
  • 39. 依赖注入--手工装配在java代码中使用@Autowired或@Resource注解方式进行装配,这两个注解的区别是:@Autowired 默认按类型装配,@Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配。 @Autowired private PersonDao personDao;//用于字段上 @Autowired public void setOrderDao(OrderDao orderDao) {//用于属性的setter方法上 this.orderDao = orderDao; } @Autowired注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false @Autowired (required=false)。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。如下: @Autowired @Qualifier("personDaoBean") private PersonDao personDao; @Resource注解和@Autowired一样,也可以标注在字段或属性的setter方法上,但它默认按名称装配。名称可以通过@Resource的name属性指定,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。 @Resource(name=“personDaoBean”) private PersonDao personDao;//用于字段上 注意:如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。
  • 40. 依赖注入--手工装配-注解装配Autowiredpublic class Computer {//计算机类 @Autowired private USB usb; @Autowired private Iprint print; @Autowired private Memory memory; public void showHardWareInfo(){ usb.info("移动硬盘"); print.info("彩色打印机"); memory.info("金士顿内存条"); } //不用再写set、get方法 }bean.xml配置 --> @Test public void getComputer(){//模拟计算机组装测试方法 Computer computer = (Computer)context.getBean("computerBean"); computer.showHardWareInfo(); }与xml配置文件中配置引用的Bean的区别是: Computer Bean中不用再配置引用的Bean;这是优点;缺点是还是要在spring配置文件中配置uDisk,colorPrint,memory,computerBean这些Bean
  • 41. 依赖注入--手工装配-注解装配Resourcepublic class Computer { private USB usb; private Iprint print; private Memory memory; public void showHardWareInfo(){ usb.info("移动硬盘"); print.info("彩色打印机"); memory.info("金士顿内存条"); } @Resource(name="uDisk") public void setUsb(USB usb) { this.usb = usb; } @Resource(name="colorPrint") public void setPrint(Iprint print) { this.print = print; } @Resource(name="memory") public void setMemory(Memory memory) { this.memory = memory; } //不用再写set、get方法 } bean.xml配置 @Test public void getComputer(){//模拟计算机组装测试方法 Computer computer = (Computer)context.getBean("computerBean"); computer.showHardWareInfo(); }与xml配置文件中配置引用的Bean的区别是: Computer Bean中不用再配置引用的Bean;这是优点;缺点是还是要在spring配置文件中配置uDisk,colorPrint,memory,computerBean这些Bean
  • 42. 依赖注入--自动装配依赖对象对于自动装配,大家了解一下就可以了,实在不推荐大家使用。例子: autowire属性取值如下: byType:按类型装配,可以根据属性的类型,在容器中寻找跟该类型匹配的bean。如果发现多个,那么将会抛出异常。如果没有找到,即属性值为null。 byName:按名称装配,可以根据属性的名称,在容器中寻找跟该属性名相同的bean,如果没有找到,即属性值为null。 constructor与byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。 autodetect:通过bean类的自省机制(introspection)来决定是使用constructor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式。
  • 43. 通过在classpath自动扫描方式把组件纳入spring容器中管理前面的例子我们都是使用XML的bean定义来配置组件。在一个稍大的项目中,通常会有上百个组件,如果这些这组件采用xml的bean定义来配置,显然会增加配置文件的体积,查找及维护起来也不太方便。spring2.5为我们引入了组件自动扫描机制,他可以在类路径底下寻找标注了@Component、@Service、@Controller、@Repository注解的类,并把这些类纳入进spring容器中管理。它的作用和在xml文件中使用bean节点配置组件是一样的。要使用自动扫描机制,我们需要打开以下配置信息: 其中base-package为需要扫描的包(含子包)。 @Service用于标注业务层组件、 @Controller用于标注控制层组件(如struts中的action)、@Repository用于标注数据访问组件,即DAO组件。而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
  • 44. 自动扫描式注入Bean(优点:配置文件中不用再配置bean)//接口设计 public interface USB { //USB接口信息 public void info(String content); } public interface Iprint { //打印机接口信息 public void info(String content); }@Component("memory") public class Memory {//内存类 public void info(String config) { System.out.println("内存配置:"+config); } } @Component("computer") public class Computer { //面向接口编程,下面的 组件都是接口,不是类@Resource(name="usb") private USB usb; @Resource(name="colorPrint") private Iprint print; @Resource(name="memory") private Memory memory; public void showHardWareInfo(){ usb.info("移动硬盘"); print.info("彩色打印机"); memory.info("金士顿内存条"); } //上面的注解类也可以写在set方法上面 //不用再写set、get方法 }Spring配置文件配置 @Test public void getComputer(){ Computer computer = (Computer)context.getBean("computer"); computer.showHardWareInfo(); }@Component(value="usb") public class UDisk implements USB {//接口实现类 public void info(String config) { System.out.println("usb接口配置:"+config); } } @Component("colorPrint") public class ColorPrint implements Iprint { public void info(String config) { System.out.println("彩色打印机:"+config); } }
  • 45. 静态代理//接口设计 public interface Animal { public abstract void eat(String food); public abstract String type(); }//AnimalWrapper类是包装类(代理类) public class AnimalWrapper { private Animal animal; public AnimalWrapper(Animal animal){ this.animal = animal; } public void eat(String food) { System.out.println("+++Wrapped 前置通知!+++"); animal.eat(food); System.out.println("+++Wrapped 后置通知!+++"); } public String type() { System.out.println("---Wrapped Before!---"); String type = animal.type(); System.out.println("---Wrapped After!---"); return type; } }public class Monkey implements Animal {//接口实现类 public String type() { String type = "哺乳动物"; System.out.println(type); return type; } public void eat(String food) { System.out.println("The food is"+food+ "!"); } }@Test public static void main(String[] args) { AnimalWrapper wrapper = new AnimalWrapper(new Monkey()); wrapper.eat("香蕉"); }
  • 46. 静态代理的缺点前面我们通过包装的模式完成了对Animal 所有子类 的静态代理,在代理方法中,你可以加入一些自己的额外 的处理逻辑,就像上面的控制台输出语句一样。那么 Spring的前置、后置、环绕方法通知,通过这种方 式可以有限的模拟出来,以Spring 的声明式事务为 例,无非就是在调用包装的目标方法之前处开启事 务,在之后提交事务,这样原有的业务逻辑没有受到 任何事务管理代码的侵入。这种方式的静态代理,缺 点就是当Animal接口中增加了新的方法,那么包装 类中也必须增加这些新的方法。
  • 47. JDK动态代理public class JDKProxy implements InvocationHandler { private Object targetObject;//要代理的目标对象 public Object createProxyInstance(Object targetObject){ this.targetObject = targetObject; /* * 第一个参数设置代码使用的类装载器,一般采用跟目标类相同的类装载器 * 第二个参数设置代理类实现的接口 * 第三个参数设置回调对象,当代理对象的方法被调用时,会委派给该参数指定对象的invoke方法 */ return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), this.targetObject.getClass().getInterfaces(), this); } //method是被拦截到的方法, args是该方法所需要传的参数 public Object invoke(Object proxy, Method method, Object[] args)throws Throwable { return method.invoke(this.targetObject, args);//把方法调用委派给目标对象 } } 当目标类实现了接口,我们可以使用jdk的Proxy来生成代理对象。
  • 48. JDK动态代理案例:/** * 业务接口 * @author Administrator */ public interface ILogin { public boolean login(String ipAddress,String name,String password); }/** * 目标类(接口实现类) * @author Administrator * */ public class LoginImpl implements ILogin { public boolean login(String ipAddress,String name,String password){ System.out.println("进入目标类的方法....."); System.out.println(LoginImpl.class.getName()+"用户:"+name+"在"+ipAddress+"登录"); return true; } }代理类 在后面
  • 49. JDK动态代理案例(续)-代理类public class JDKProxy implements InvocationHandler{ private Object targetObject;//要代理的目标对象 public Object createProxyInstance(Object targetObject){//创建代理对象 this.targetObject = targetObject;//赋值,下面invoke方法用到这个对象 /* * 参数1:设置代码使用的类装载器,一般采用跟目标类相同的类装载器 * 参数2:设置代理类实现的接口 * 参数3:设置回调对象,当代理对象的方法被调用时(这里对应的是LoginImpl类中的login方法),会委派给该参数指定对象的invoke方法 */ Object proxyInstance = Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(),this); return proxyInstance; } //通知的概念就是要实现某个功能(这个方法就是环绕通知:也就是在目标对象的方法执行前后各插入一些逻辑进行包围[环绕]) public Object invoke(Object proxy, Method method, Object[] args)throws Throwable { System.out.println("目标对象方法名:"+method.getName()); for (Object e : args) { System.out.println("方法参数类型:"+e.getClass().getName()+"方法参数值:"+e); } Object result = null; boolean flag = beforeLogin(args);//登录之前做的事情(前置通知),就是invoke之前的方法调用 if(flag){//如果IP地址是100[192.168.1.100],则可以登录 try { //调用目标对象的方法 result = method.invoke(targetObject,args);//这里其实就是调用LoginImpl类中的login方法 } catch (Exception e) { doExceptionAdvice();//例外通知 e.printStackTrace(); }finally{ dofinallyAdvice();//最终通知 } }else{ System.out.println("Oh,my god!你没有权限登录"); } afterLogin(args);//登录之后做的事情(后置通知) return result; }
  • 50. 前置通知、后置通知、最终通知方法private void dofinallyAdvice() { System.out.println("------最终通知"); } private void doExceptionAdvice() { System.out.println("------例外通知"); } private void afterLogin(Object[] args) { System.out.println("------后置通知"); if(args!=null && !args.equals("")){ //…… } System.out.println("登录完成,写入日志...."); } //所谓的通知:就是拦截到方法之后要做的事情 private boolean beforeLogin(Object[] args) { System.out.println("---------------前置通知"); boolean flag = false; if(args!=null && !args.equals("")){//判断位置是否能反过来?,NullPointException String ipAddress = (String)args[0]; int lastPointIndex = ipAddress.lastIndexOf("."); String ip = ipAddress.substring(lastPointIndex+1); if(ip.equals("100")){ System.out.println("ip:100;允许登录..."); flag = true; } } return flag; }
  • 51. JDK动态代理测试方法public class JDKProxyTest { @Test public void test() { //ILogin instance = new LoginImpl();//不用代理类时直接获取 JDKProxy proxy = new JDKProxy(); //通过代理类JDKProxy来获取目标类的对象,多了一个中间环境 ILogin instance = (ILogin)proxy.createProxyInstance(new LoginImpl()); instance.login("192.168.1.100", "lisi", "lisi"); } }
  • 52. JDK动态代理的缺点 JDK 的动态代理有个缺点,那就是不能对类进行代理,只能对接口进行代理,要代理的目标对象必须实现某个接口;想象一下我们的LoginImpl类如果没有实现任何接口,那么将无法使用这种方式进行动态代理(实际上是因为$Proxy4 这个类继承了Proxy,JAVA 的继承不允许出现多个父类)。但准确的说这个问题不应该是缺点,因为良好的系统,每一个类都是应该有一个接口的Object proxyInstance = Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(),this); System.out.println(proxyInstance.getClass().getName());
  • 53. 使用CGLIB生成代理public class CGLIBProxy implements MethodInterceptor { private Object targetObject;//要代理的目标对象 public Object createProxyInstance(Object targetObject){ this.targetObject = targetObject; Enhancer enhancer = new Enhancer();//该类用于生成代理对象 enhancer.setSuperclass(this.targetObject.getClass());//设置父类 enhancer.setCallback(this);//设置回调用对象为本身 return enhancer.create(); } public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { return methodProxy.invoke(this.targetObject, args); } } CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。
  • 54. CGLIB生成代理案例import java.lang.reflect.Method; import net.sf.cglib.proxy.*; //需要cglib-nodep-2.1_3.jar /** * CGLIB生成代理 * @author Administrator * */ public class CGLIBProxy implements MethodInterceptor { public Object createCGLIBProxyInstance(Object targeObject){ Enhancer hancer = new Enhancer(); hancer.setSuperclass(targeObject.getClass()); hancer.setCallback(this); return hancer.create(); } public Object intercept(Object object, Method method, Object[] args,MethodProxy methodProxy) throws Throwable { return methodProxy.invokeSuper(object, args); } }/** * 目标类,目标不用实现接口,这是 * CGLIBProxy代理跟JDK动态代理的区别 * @author Administrator * */ public class UserLogin { public boolean login(String name,String password){ System.out.println(name); return false; } }public class CGLIBProxyTest {// CGLIB生成代理测试类 @Test public void testCGLIBProxy(){ CGLIBProxy proxy = new CGLIBProxy(); UserLogin login = (UserLogin)proxy.createCGLIBProxyInstance(new UserLogin()); login.login("lisi", "qq"); } }
  • 55. AOP中的概念Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样, 类是对物体特征的抽象,而切面是横切性关注点的抽象(切面就是一个关注点的模块化,譬如:事务管理、日志记录、权限管理都是所谓的切面). joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法, 因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器) Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义. Advice(通知):所谓通知是指拦截到joinpoint(方法)之后所要做的事情就是通知. 通知分为前置通知,后置通知,异常通知,最终通知,环绕通知 Target(目标对象):要代理的目标对象 Weave(织入):指将aspects (切面)应用到target对象并导致proxy对象创建的过程称为织入. Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
  • 56. Spring代理作用可以在你执行被代理对象里面的某个method前后自动执行任意你规定的代码,而不影响method其他相邻方法的执行,使代码程序低耦合。 也可以写一个不依赖与其他模块的日志管理模块,然后用AOP来把这个模块跟数据库操作模块挂钩,在数据操作的同时也通过AOP功能自动的记录了数据库操作日志。 当不需要日志时,可直接移除而不影响其他的代码。
  • 57. 使用Spring进行面向切面(AOP)编程要进行AOP编程,首先我们要在spring的配置文件中引入aop命名空间: Spring提供了两种切面声明方式,实际工作中我们可以选用其中一种: 基于XML配置方式声明切面。 基于注解方式声明切面。
  • 58. 基于注解方式声明切面首先启动对@AspectJ注解的支持(蓝色部分):
  • 59. 基于注解方式声明切面-1@Aspect public class UserInterceptor { @Pointcut("execution (* cn.com.chongking.aop.annotation.UserServiceBean.*(..))") private void anyMethod() {}//声明一个切入点(定义要拦截的方法) //&& args(name)意思是:除了要满足上面的execution (* cn.com.chongking.aop.annotation.UserServiceBean.*(..))之外还要满足这个方法中的 //参数个数是一个;参数类型是String @Before("anyMethod() && args(name)")//anyMethod是切入点的名称(就是上面的切入点方法名);args(name)和下面方法中的name一致 public void doAccessCheck(String name) { System.out.println("前置通知:"+ name); } @AfterReturning(pointcut="anyMethod()",returning="user") public void doAfterReturning(UserInfo user) {//定义后置通知 System.out.println("后置通知:"+ user); } @After("anyMethod()") public void doAfter() { System.out.println("最终通知"); } @AfterThrowing(pointcut="anyMethod()",throwing="e") public void doAfterThrowing(Exception e) {//例外通知 System.out.println("例外通知:"+ e); } @Around("anyMethod()")//环绕通知(格式基本上是固定的) public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { //if(){//判断用户是否在权限 System.out.println("进入环绕通知方法"); Object result = pjp.proceed(); System.out.println("退出环绕通知方法"); //} return result; } }
  • 60. AOP表达式语言@Ponitcut("execution (* aop.ServiceBean.*(..))") 说明:对aop.ServiceBean类中的所有方法进行拦截;方法中的参数可以是任意类型的。 @Ponitcut("execution (* aop.service..*.*(..))") 说明:execution表示执行;第一个星号,也就是aop前面的*代表的是任何的返回值类型。aop.service代表的是对aop.service包下的类进行拦截 两个点(..)的意思是包括aop.service包下的子包也进行拦截 如果没有两个点(..),则只对aop.service包下的类进行拦截 *.*中的第一个*号代表的是类,你要对哪个类进行的拦截,这里是所有的意思 第二*代表的是方法,表示所有的方法 括号中的两个点(..)表示方法中的参数可以是任意的类型(有参数也可以,没有参数也可以,一个参数可以,多个参数也可以) @Ponitcut("execution (java.lang.String aop.ServiceBean.*(..))") 说明:只拦截返回值为java.lang.String类型的方法 @Ponitcut("execution (!void aop.ServiceBean.*(..))") 说明:只拦截返回值不是void类型的方法 @Ponitcut("execution (* aop.ServiceBean.*(java.lang.String,..))") 说明:只拦截第一个参数是java.lang.String类型,后面的参数是任意类型的(也可以没有参数)
  • 61. 基于注解方式声明切面-2//实现类 public class UserServiceBean implements UserService { public UserInfo get(Class cl,Integer uId) { System.out.println("我是getPersonName()方法"); return new UserInfo(1, "lisi", "liuzhou", 22, "lisi@qq.com"); } public void save(UserInfo user) { System.out.println("进入save()方法"); throw new RuntimeException("出问题了!"); } public void update(UserInfo user) { System.out.println("进入update()方法"); } }public interface UserService {//接口声明 public void save(UserInfo user); public void update(UserInfo user); public UserInfo get(Class cl,Integer uId); }public class InterceptorTest {//测试类 @Test public void test(){ ApplicationContext cxt = new ClassPathXmlApplicationContext("beans.xml"); UserService userService = (UserService)cxt.getBean("userService"); UserInfo user = new UserInfo(1, "lisi", "liuzhou", 22, "lisi@qq.com"); //userService.save(user);//save方法产生列外;不回进入后置通知;但是会进入最终通知 userService.update(user); //userService.get(UserInfo.class,1); } }
  • 62. 基于基于XML配置方式声明切面-类设计public class DepartServiceImpl implements DepartService { public Department get(Class cl,Integer dId) { System.out.println("我是getPersonName()方法"); return new Department("财务部", "财务部"); } public void save(Department department) { System.out.println("进入save()方法"); throw new RuntimeException("出问题了!"); } public void update(Department department) { System.out.println("进入update()方法"); } }public interface DepartService {//接口设计 public void save(Department department); public void update(Department department); public Department get(Class cl,Integer dId); }public class DepartInterceptor { //切面(注意下面这些方法没有参数) public void doAccessCheck() {//前置通知 System.out.println("前置通知:"); } public void doAfterReturning() {//定义后置通知 System.out.println("后置通知:"); } public void doAfter() { System.out.println("最终通知"); } public void doAfterThrowing() {//例外通知 System.out.println("例外通知:"); } //环绕通知(格式基本上是固定的) public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable { //if(){//判断用户是否在权限 System.out.println("进入环绕通知方法"); Object result = pjp.proceed(); System.out.println("退出环绕通知方法"); //} return result; } }
  • 63. 基于基于XML配置方式声明切面-配置文件
  • 64. Spring+JDBC组合开发(注解方式管理事务)--数据源配置
  • 65. Spring+JDBC组合开发(注解方式管理事务)--事务配置………………………… ………………………………………. ………………………………………..
  • 66. Spring+JDBC组合开发(注解方式管理事务)--接口设计public interface DepartService {//接口定义 public void save(Department depart); public void update(Department depart); public Department getDepart(Integer dId); public List getDeparts(); public void delete(Integer dId)throws Exception; }
  • 67. Spring+JDBC组合开发(注解方式管理事务)--类的实现//官方推荐使用基于注解的事务管理;而非xml配置方式管理事务(基于注解的事务管理可以对要开启的事务做具体的事务属性配置) @Transactional //启用基于注解的事物(声明式的事物);所有方法除非显示声明不开启事务;否则默认开启事务. public class DepartServiceImpl implements DepartService { //jdbc模版 private JdbcTemplate jdbcTemplate; //在personServiceBean中通过set方式注入DataSource public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } //默认情况下运行期异常RuntimeException;spring容器会回滚事务(Exception则不会回滚事务) /*public void delete(Integer dId) throws RuntimeException{ jdbcTemplate.update("delete from t_department where id=?", new Object[]{dId}, new int[]{java.sql.Types.INTEGER}); throw new RuntimeException("运行期异常;spring容器将回滚事务;数据仍在"); }*/ /*@Transactional(noRollbackFor=RuntimeException.class)//手动设置改变spring的事务配置属性值;让事务不回滚(noRollbackFor) public void delete(Integer dId) throws RuntimeException{ jdbcTemplate.update("delete from t_department where id=?", new Object[]{dId}, new int[]{java.sql.Types.INTEGER}); throw new RuntimeException("运行期异常;手动设置改变spring的事务配置属性值;让事务不回滚(noRollbackFor);数据被删除"); }*/ /*public void delete(Integer dId) throws Exception{ jdbcTemplate.update("delete from t_department where id=?", new Object[]{dId}, new int[]{java.sql.Types.INTEGER}); throw new Exception("非运行期异常;spring容器不会回滚事务;数据将被删除"); }*/ @Transactional(rollbackFor=Exception.class)//可以手动设置改变spring的事务配置属性值;让事务回滚 public void delete(Integer dId) throws Exception{ jdbcTemplate.update("delete from t_department where id=?", new Object[]{dId}, new int[]{java.sql.Types.INTEGER}); throw new Exception("非运行期异常;但是已经手动改变事务属性(rollbackFor=Exception.class);spring容器会回滚事务;数据将被保留"); }
  • 68. Spring+JDBC组合开发(注解方式管理事务)--类的实现(续)@Transactional(propagation=Propagation.NOT_SUPPORTED)//查询方法;不需要是用事务,提高效率 public Department getDepart(Integer dId) { return (Department)jdbcTemplate.queryForObject("select * from t_department where id=?", new Object[]{dId}, new int[]{java.sql.Types.INTEGER}, new DepartmentRowMapper()); } @Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true) @SuppressWarnings("unchecked") public List getDeparts() { return (List)jdbcTemplate.query("select * from t_department",new DepartmentRowMapper()); } public void save(Department depart) { jdbcTemplate.update("insert into t_department (name,remark) values(?,?)", new Object[]{depart.getName(),depart.getRemark()}, new int[]{java.sql.Types.VARCHAR,java.sql.Types.VARCHAR}); //下面是事物测试 @Transactional /*jdbcTemplate.update("insert into t_department (name,remark) values(?,?)", new Object[]{depart.getName(),depart.getRemark()}, new int[]{java.sql.Types.VARCHAR,java.sql.Types.VARCHAR}); depart.setName("name字段的值即将被截断;如果使用了事物处理,两条语句要么成功;要么失败;这两个动作作为一个整体事物提交;而不是单独提交;要让错误出现,测试时需要将name字段的长度设置小一点即可。"); jdbcTemplate.update("insert into t_department (name,remark) values(?,?)", new Object[]{depart.getName(),depart.getRemark()}, new int[]{java.sql.Types.VARCHAR,java.sql.Types.VARCHAR});*/ } public void update(Department depart) { jdbcTemplate.update("update t_department set name=?,remark=? where id=?", new Object[]{depart.getName(),depart.getRemark(), depart.getId()}, new int[]{java.sql.Types.VARCHAR,java.sql.Types.VARCHAR,java.sql.Types.INTEGER}); }
  • 69. Spring+JDBC组合开发(注解方式管理事务)--测试public class SpringJdbcAnnotationTest { private DepartService departService; @Before public void before() { ApplicationContext cxt = new ClassPathXmlApplicationContext("jdbcAnnotationTransaction.xml"); departService = (DepartService) cxt.getBean("personServiceBean"); } @Test public void save(){ for(int i=0; i<5; i++) departService.save(new Department("学术部"+i,"学术部"+i)); } @Test public void getDepart(){ Department depart = departService.getDepart(1); System.out.println(depart.getName()); }
  • 70. Spring+JDBC组合开发(注解方式管理事务)--测试(续)@Test public void update(){ Department depart = departService.getDepart(2); depart.setName("财务部"); depart.setRemark("财务部"); departService.update(depart); } @Test public void delete(){ try { departService.delete(38); } catch (Exception e) { e.printStackTrace(); } } @Test public void getDeparts(){ for(Department depart : departService.getDeparts()){ System.out.println(depart.getName()); } } }
  • 71. Spring+JDBC组合开发(基于XML方式配置事务)—数据源配置
  • 72. Spring+JDBC组合开发(基于XML方式配置事务)—事务配置
  • 73. Spring+JDBC组合开发(基于XML方式配置事务)—测试DepartServiceImpl类的上面没有了声明式事务;方法上面不再制定具体的事务属性(而是由xml中定义的规则来拦截并处理事务);其它和注解方式一样(包括测试类和测试方法)
  • 74. 事务传播属性REQUIRED:业务方法需要在一个事务中运行。如果方法运行时,已经处在一个事务中,那么加入到该事务,否则为自己创建一个新的事务(开启事务的方法绝大部分使用这种类型事务)。 NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行。 REQUIRESNEW:属性表明不管是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行。 MANDATORY:该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出例外。 SUPPORTS:这一事务属性表明,如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分。如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行。 Never:指定业务方法绝对不能在事务范围内执行。如果业务方法在某个事务中执行,容器会抛出例外,只有业务方法没有关联到任何事务,才能正常执行。 NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效
  • 75. 数据库系统提供了四种事务隔离级数据库系统提供了四种事务隔离级别供用户选择。不同的隔离级别采用不同的锁类型来实现,在四种隔离级别中,Serializable的隔离级别最高,Read Uncommited的隔离级别最低。大多数据库默认的隔离级别为Read Commited,如SqlServer,当然也有少部分数据库默认的隔离级别为Repeatable Read ,如Mysql Read Uncommited:读未提交数据(会出现脏读,不可重复读和幻读)。 Read Commited:读已提交数据(会出现不可重复读和幻读) Repeatable Read(一般用这个即可):可重复读(会出现幻读) Serializable:串行化 脏读:一个事务读取到另一事务未提交的更新新据。 不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。 幻读:一个事务读取到另一事务已提交的insert数据。
  • 76. Spring2.5+Hibernate3.3+Struts1.3整合开发-jar包hibernate核心安装包下的: hibernate3.jar lib\required\*.jar lib\optional\ehcache-1.2.3.jar hibernate 注解安装包下的 lib\test\slf4j-log4j12.jar Spring安装包下的 dist\spring.jar dist\modules\spring-webmvc-struts.jar lib\jakarta-commons\commons-logging.jar、commons-dbcp.jar、commons-pool.jar lib\aspectj\aspectjweaver.jar、aspectjrt.jar lib\cglib\cglib-nodep-2.1_3.jar lib\j2ee\common-annotations.jar lib\log4j\log4j-1.2.15.jar Struts 下载struts-1.3.8-lib.zip,需要使用到解压目录下的所有jar,建议把jstl-1.0.2.jar和standard-1.0.2.jar更换为1.1版本。Spring中已经存在一个antlr-2.7.6.jar,所以把struts中的antlr-2.7.2.jar删除,避免jar冲突。 数据库驱动jar
  • 77. Spring2.5+Hibernate3.3+Struts1.3整合开发-spring配置//配置数据源 ......................................................... 略 com/entity/bean/Person.hbm.xml hibernate.dialect=org.hibernate.dialect.MySQL5Dialect hibernate.hbm2ddl.auto=update hibernate.show_sql=false hibernate.format_sql=false
  • 78. Spring2.5+Hibernate3.3+Struts1.3整合开发实体bean配置模版.hbm.xml
  • 79. Spring2.5+Hibernate3.3+Struts1.3整合开发Hibernate二级缓存的配置 com/cjkj/bean/Person.hbm.xml hibernate.dialect=org.hibernate.dialect.MySQL5Dialect hibernate.hbm2ddl.auto=update hibernate.show_sql=false hibernate.format_sql=false hibernate.cache.use_second_level_cache=true hibernate.cache.use_query_cache=false hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider
  • 80. Spring2.5+Hibernate3.3+Struts1.3整合开发在需要缓存的实体bean配置文件中加入缓存配置项 usage说明了缓存的策略,region指定缓存的区域名(不填写时默认使用缓存框架中的区域名)
  • 81. Spring2.5+Hibernate3.3+Struts1.3整合开发Ehcache默认的配置文件ehcache.xml(放在类路径下) defaultCache节点为缺省的缓存策略 maxElementsInMemory 内存中最大允许存在的对象数量 eternal 设置缓存中的对象是否永远不过期 overflowToDisk 把溢出的对象存放到硬盘上 timeToIdleSeconds 指定缓存对象空闲多长时间就过期,过期的对象会被清除掉 timeToLiveSeconds 指定缓存对象总的存活时间 diskPersistent 当jvm结束是是否持久化对象 diskExpiryThreadIntervalSeconds 指定专门用于清除过期对象的监听线程的轮询时间
  • 82. Spring2.5+Hibernate3.3+Struts1.3整合开发 在web容器中实例化spring容器, contextConfigLocation classpath:beans.xml org.springframework.web.context.ContextLoaderListener
  • 83. Spring2.5+Hibernate3.3+Struts1.3整合开发在web容器中配置struts, struts org.apache.struts.action.ActionServlet config /WEB-INF/struts-config.xml 0 struts *.do
  • 84. Spring2.5+Hibernate3.3+Struts1.3整合开发如果action没有交给spring管理时,我们通过下面语句获取spring容器实例 WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(this.getServlet().getServletContext()); 把action交给spring管理后,我们可以使用依赖注入在action中注入业务层的bean。确保action的path属性值与bean的名称相同。 Spring 配置: 在struts配置文件中添加进spring的请求控制器,该请法语控制器会先根据action的path属性值到spring容器中寻找跟该属性值同名的bean。如果寻找到即使用该bean处理用户请求
  • 85. Spring2.5+Hibernate3.3+Struts1.3整合开发org.springframework.web.struts.DelegatingRequestProcessor 的处理过程。 假设用户请求的路径为:/person/list.do ActionServletDelegatingRequestProcessor请求处理器Spring容器 Action交给了容器管理
  • 86. Spring2.5+Hibernate3.3+Struts1.3整合开发使用spring解决struts1.3乱码问题。 encoding org.springframework.web.filter.CharacterEncodingFilter encoding UTF-8 encoding /*
  • 87. Spring2.5+Hibernate3.3+Struts1.3整合开发使用spring解决hibernate因session关闭导致的延迟加载例外问题。 OpenSessionInViewFilter org.springframework.orm.hibernate3.support.OpenSessionInViewFilter OpenSessionInViewFilter /*
  • 88. Spring2.5+JPA+Struts1.3整合开发这里JPA的实现采用hibernate,需要使用到下面的jar文件 Hiberante核心包(8个文件) hibernate-distribution-3.3.1.GA --------------------------------------------- hibernate3.jar lib\bytecode\cglib\hibernate-cglib-repack-2.1_3.jar lib\required\*.jar Hiberante注解包(3个文件):hibernate-annotations-3.4.0.GA ------------------------------------------------------------------------------------ hibernate-annotations.jar lib\ejb3-persistence.jar、hibernate-commons-annotations.jar Hibernate针对JPA的实现包(3个文件):hibernate-entitymanager-3.4.0.GA ------------------------------------------------------------------------------------------------------ hibernate-entitymanager.jar lib\test\log4j.jar、slf4j-log4j12.jar
  • 89. Spring2.5+JPA+Struts1.3整合开发JPA规范要求在类路径的META-INF目录下放置persistence.xml,文件的名称是固定的,配置模版如下:
  • 90. Spring2.5+JPA+Struts1.3整合开发在spring中配置EntityManagerFactory
  • 91. Spring2.5+JPA+Struts1.3整合开发使用spring解决JPA因EntityManager关闭导致的延迟加载例外问题。 Spring OpenEntityManagerInViewFilter org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter Spring OpenEntityManagerInViewFilter /*
  • 92. Spring2.5+Hibernate3.3+Struts2整合开发使用到struts2的lib目录下所有不带-plugin结尾的jar文件,但除了struts2-spring-plugin-2.0.11.1.jar
  • 93. Spring2.5+Hibernate3.3+Struts2整合开发在web容器中实例化spring容器和配置struts2 contextConfigLocation classpath:beans.xml org.springframework.web.context.ContextLoaderListener struts2 org.apache.struts2.dispatcher.FilterDispatcher struts2 /*
  • 94. Spring2.5+Hibernate3.3+Struts2整合开发struts2的配置文件模版struts.xml /WEB-INF/page/message.jsp /WEB-INF/page/persons.jsp /WEB-INF/page/add_person.jsp /WEB-INF/page/edit_person.jsp
  • 95. Spring2.5+Hibernate3.3+Struts2整合开发struts2的标签 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib uri="/struts-tags" prefix="s" %> 姓名:
    , 修改