Spring 总结笔记

bjut_zjh

贡献于2014-06-20

字数:112096 关键词: Spring JEE框架

 一:Spring第一天 1:导入spring相关的jar包 (1)spring-framework-3.0.2.RELEASE(核心包) 写法:org.springframework…….. (2)spring第三方依赖包 spring-framework-3.0.2.RELEASE-dependencies.zip 写法:com.springsource.org.apache.log4j-sources-1.2.15.jar(第三方包) Spring开发需要哪些jar包 2:spring的核心容器 默认的写法:applicationContext.xml(默认文件放置到src下,但是也可以更改路径) 可以自定义命名:beans.xml 在beans.xml中引入约束的文件 如果你发现在beans.xml中不能显示对应的提示:此时xsd的文件没有被加载到myeclipse中 3:spring加载容器的方式 核心对象: 4:控制反转(IOC) (1)概念:所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转,目的是为了获得更好的扩展性和良好的可维护性。 (2)代码: 存在一个Boy对象: public class Boy { public void display(){ System.out.println("我是一个优秀的Boy!"); } } 在spring容器中定义: 使用App进行测试: package cn.itcast.a_ioc; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { public static void main(String[] args) { // Boy boy = new Boy(); // boy.display(); /**使用spring容器构造boy对象*/ //使用applicationContext加载spring容器 /** * new ClassPathXmlApplicationContext():表示默认加载类路径下的applicationContext.xml * new ClassPathXmlApplicationContext("beans.xml"):表示类路径下的beans.xml * new ClassPathXmlApplicationContext("cn/itcast/a_ioc/beans.xml"):表示类路径下的cn.itcast.a_ioc包下的beans.xml */ ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/a_ioc/beans.xml"); //获取容器下的id指定的对象 Boy boy = (Boy) ac.getBean("boy"); boy.display(); } } 5:依赖注入(DI) 概念:所谓依赖注入就是指:在运行期,由外部容器动态地将依赖对象注入到另一个对象的组件中。 类中定义:创建2个对象,一个Boy对象和一个Girl对象 package cn.itcast.b_di; public class Boy { public void display(){ System.out.println("我是一个优秀的Boy!"); } } public class Girl { Boy boy; //完成依赖注入,必须的set方法 public void setBoy(Boy boy) { this.boy = boy; } public void kiss(){ boy.display(); System.out.println("执行Kiss的方法!"); } } 在spring容器中定义: 最后使用app进行测试: package cn.itcast.b_di; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { public static void main(String[] args) { // Boy boy = new Boy(); // boy.display(); //使用applicationContext加载spring容器 ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/b_di/beans.xml"); //获取容器下的id指定的对象 Girl girl = (Girl) ac.getBean("girl"); girl.kiss(); } } 原理: 其实 IOC(控制反转)是利用java的反射思想实现的 DI(依赖注入)是利用java的内省机制实现 详情请看代码:itcast1206_b_springParse 6:spring容器中bean节点id和name的区别 项目开发的时候,强烈要求用id,因为id可以表示惟一引用 7:加载spring容器的3种方式 /** * 方法一: 在类路径下寻找配置文件来实例化容器 */ //使用applicationContext加载spring容器 //如果定义成数组一次可以加载多个spring容器 //ApplicationContext ac = new ClassPathXmlApplicationContext(new String[]{"cn/itcast/b_di/beans.xml"}); /** * 方法二: 在文件系统路径下寻找配置文件来实例化容器 */ //ApplicationContext ac = new FileSystemXmlApplicationContext(new String[]{"D:\\workspaceSpring\\itcast1206_a_springBean\\src\\cn\\itcast\\b_di\\beans.xml"}); /** * 方法三:使用BeanFactory */ BeanFactory ac = new XmlBeanFactory(new FileSystemResource("D:\\workspaceSpring\\itcast1206_a_springBean\\src\\cn\\itcast\\b_di\\beans.xml")); 8:三种实例化bean的方式 定义一个接口和实现类 接口: public interface IUserService { public void saveUser(); } 实现类 public class UserServiceImpl implements IUserService { public void saveUser() { System.out.println("访问UserServiceImpl类的saveUser()的方法"); } } (1)使用接口和类,直接在spring容器中创建一个类,使用接口进行操作 在容器中定义: 注意:class一定要是类,不能使用接口 (2)使用静态工厂方法实例化(简单工厂模式) 1:Spring容器中定义: 2: UserObject对象中提供一个静态的的方法,用来创建对象 public class UserObject { //静态的方法 public static IUserService createUser(){ return new UserServiceImpl(); } } (3)使用实例工厂方法实例化(工厂方法模式): 1:Spring容器中定义: 2:UserObject中定义 public class UserObject { //静态的方法 public IUserService createUser(){ return new UserServiceImpl(); } } 3:使用App调用的形式 ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/e_method/beans.xml"); IUserService userService = (IUserService) ac.getBean("user"); userService.saveUser(); 9:bean 的作用域 在spring容器中存在1个scope的属性,例如: * singleton(单例:默认值) 表示创建UserServiceImpl对象,容器只创建一次 * lazy-init属性(只针对单例有效): 如果在spring容器中设置: 表示此时再使用 ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/f_scope/beans.xml"); 加载spring容器,将不会执行UserServiceImpl类的构造方法。 当执行到ac.getBean("userService");才会执行UserServiceImpl类的方法。可以理解成懒加载对象 如果在spring容器中设置: 此时表示只要调用加载spring容器的代码,就会执行UserServiceImpl类的构造方法。 * 当beans下配置:default-lazy-init="true",此时对beans下的所有bean节点都有效,如果都进行设置,局部将覆盖全局 * prototype (多例) 表示每次加载ac.getBean("userService");都会执行UserServiceImpl的构造方法,容器此时可以创建多个对象。 注意:用途:如果通过spring创建struts的Action实例的时候,就必须指定prototype类型,因为struts2是多实例多线程,否则struts2的值栈会出现问题。 10:创建bean的生命周期(了解) 第一步:这是UserServiceImpl的构造方法! 第二步:在容器创建对象,通过set方法注入到目标对象中! 第三步:实现BeanNameAware接口,调用setBeanName的方法! 第四步:实现BeanFactoryAware接口,调用setBeanFactory的方法! 第五步:类实现BeanPostProcessor,调用postProcessBeforeInitialization()方法! 第六步:实现InitializingBean接口,调用afterPropertiesSet的方法! 第七步:在bean节点下定义init-method指定的方法! 第八步:类实现BeanPostProcessor,调用postProcessAfterInitialization()方法! 第九步:访问UserServiceImpl类的saveUser()的方法! 第十步:实现DisposableBean接口,调用destroy的方法! 第十一步:在bean节点下定义destroy-method指定的方法! 查看:代码 重点:1,2,9步,7,11步 11:依赖注入 (1)xml方式 1)set方法完成注入 * 定义UserServiceImpl类: /**基本数据类型和String*/ private Integer uid; private String uname; /**注入对象*/ private Person person; /**list集合*/ private List list; /**数组*/ private Object [] arrays; /**set集合*/ private Set set; /**map集合*/ private Map map; /**Properties集合*/ private Properties properties; /**集合属性*/ private List nullList = new ArrayList(); public void setNullList(List nullList) { this.nullList = nullList; } public void setProperties(Properties properties) { this.properties = properties; } public void setMap(Map map) { this.map = map; } public void setSet(Set set) { this.set = set; } public void setArrays(Object[] arrays) { this.arrays = arrays; } public void setList(List list) { this.list = list; } public void setPerson(Person person) { this.person = person; } public void setUid(Integer uid) { this.uid = uid; } public void setUname(String uname) { this.uname = uname; } public void saveUser() { System.out.println("访问UserServiceImpl类的saveUser()的方法!"); System.out.println("uid:"+uid); System.out.println("uname:"+uname); System.out.println("person.getName:"+person.getName()); System.out.println("person.getAge:"+person.getAge()); if(list!=null && list.size()>0){ for(Object o:list){ System.out.println("list:"+o); } } if(arrays!=null && arrays.length>0){ for(Object o:arrays){ System.out.println("array:"+o); } } Iterator ite = set.iterator(); while(ite.hasNext()){ Object o = ite.next(); System.out.println("set:"+o); } System.out.println("map001:"+map.get("mapKey001")); System.out.println("map002:"+map.get("mapKey002")); System.out.println("map003:"+map.get("mapKey003")); System.out.println("prop001:"+properties.get("prop001")); System.out.println("prop002:"+properties.get("prop002")); System.out.println("nullList:"+nullList); } * Spring容器中定义 list001 list002 array001 array002 set001 set002 mapValue002 propValue001 propValue002 (2)构造函数完成注入 * 创建UserServiceImpl类 public class UserServiceImpl implements IUserService { private Integer id; private String name; private String education; //使用构造函数注入 public UserServiceImpl(String name,Integer id){ System.out.println("先String,再Integer"); this.id = id; this.name = name; } //使用构造函数注入 public UserServiceImpl(Integer id,String name){ System.out.println("先Integer,再String"); this.id = id; this.name = name; } public UserServiceImpl(String name,String education){ this.name = name; this.education = education; } public void saveUser() { System.out.println("访问UserServiceImpl类的saveUser()的方法!"); System.out.println("id:"+id); System.out.println("name:"+name); System.out.println("education:"+education); } } * spring容器中定义: 10 如花 12:bean之间的关系(了解知识): 1:继承(了解) 2:依赖(了解) 3:引用(了解) 此时Student的代码为: public class Student implements ApplicationContextAware { private String bookId; private ApplicationContext applicationContext; public void setBookId(String bookId) { this.bookId = bookId; } public void readBook(){ Book book = (Book)applicationContext.getBean(bookId); System.out.println(book.getName()); } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } 注意:如果1个类实现了ApplicationContextAware接口,此时会要求实现一个setApplicationContext的方法,此时表示可以通过spring底层将applicationContext对象返回,赋值给Student类中private ApplicationContext applicationContext; 好处:就是在多个类中,调用spring容器定义的bean,不用每次都使用 ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/j_relation/beans.xml");而是在Student类中直接通过Book book = (Book)applicationContext.getBean(bookId);直接获取 13:使用多个spring容器 红色框,是项目应用比较多的 实例代码: 在:cn.itcast.j_relation.beans.xml中添加: 14:依赖注入 (3)注解 方式一:使用@Autowired和@Qualifier 在spring容器中定义: 此时在UserServiceImpl类中 public class UserServiceImpl implements IUserService { /** * Autowired标注在字段上 * @Autowired注解默认按类型进行装配 * 获取该注解标注的字段的类型---IUserDao类型 * 以该类型为条件到spring容器(beans.xml)中去查找bean的id节点的类型是IUserDao类型. * 找到以后,获取该节点对应的对象,利用反射直接为userDao变量赋值 @Qualifier(“userDao”)该注解以名字为条件查找依赖对象 * 以该注解的的参数userDao条件到spring容器(beans.xml)中去查找bean的id节点的 值是userDao的对象 * 找到以后,获取该节点对应的对象, 利用反射直接为userDao变量赋值 如果不存在该名称,抛出异常 */ //@Autowired @Qualifier("userDao") private IUserDao userDao; /** * Autowired标注在setter方法上 @Autowired注解默认按类型进行装配 * 获取 setUserDao()方法的参数的类型---UserDao类型 * 以该类型为条件到spring容器(beans.xml)中去查找bean的id节点的类型是UserDao类型. * 找到以后,获取该节点对应的对象, 把该对象作为实参传递给该setUserDao(IUserDao userDao)的形参. * * @Qualifier("userDao")该注解以名字为条件查找依赖对象 * 以该注解的的参数userDao条件到spring容器(beans.xml)中去查找bean的id节点的 值是userDao的对象 * 找到以后,获取该节点对应的对象, 把该对象作为实参传递给该setUserDao(IUserDao userDao)的形参. * 如果不存在该名称,抛出异常 */ @Autowired @Qualifier("userDao") public void setUserDao(IUserDao userDao) { this.userDao = userDao; } public void saveUser() { System.out.println("执行UserServiceImpl类的saveUser()的方法!"); userDao.save(); } } 总结:如果单独使用@Autowired,此时按照类型到spring容器中匹配,如果再配合@Qualifier("userDao"),此时就使用userDao的作为名称到spring容器中进行匹配 方式二:使用@Resource * 在spring容器中定义: * 在UsrServiceImpl类中定义: package cn.itcast.service; import javax.annotation.Resource; import cn.itcast.dao.IUserDao; public class UserServiceImpl implements IUserService { /** * Resource注解标注在字段上 * @Resource注解默认按名称装配。 * 如果没有指定name属性 * 获取该注解标注的字段值---userDao * 以该字段值为条件到spring容器(beans.xml)中去查找bean的id节点的值是 userDao的节点 * 找到以后,获取该节点对应的对象, 利用反射直接为userDao变量赋值 * 如果没有找到.并且按照默认的名称找不到依赖对象时, @Resource注解会回退到按类型装配 * 获取该注解标注的字段类型--UserDao * 以该类型为条件到spring容器(beans.xml)中去查找bean的节点的类型是UserDao类型 的对象 * 找到以后,获取该节点对应的对象, 利用反射直接为userDao变量赋值 * 如果指定name属性 只能按名称装配 * 获取name属性的值 userDao * 以该值为条件到spring容器(beans.xml)中去查找bean的id节点的值是userDao的对象 * 找到以后,获取该节点对应的对象, 利用反射直接为userDao变量赋值 * 如果不存在该名称,抛出异常 */ //@Resource(name="userDao") private IUserDao userDao; /** * resource注解标注在setter方法上 * @Resource注解默认按名称装配。 * 如果没有指定name属性 * 获取setUserDao()方法的属性名---userDao * 以该属性名为条件到spring容器(beans.xml)中去查找bean的id节点的值是userDao的节点 * 找到以后,获取该节点对应的对象, 把该对象作为实参传递给该setUserDao(IUserDao userDao)的形参. * 如果没有找到.并且按照默认的名称找不到依赖对象时,@Resource注解会回退到按类型装配 * 获取setUserDao()方法的参数类型---UserDao * 以该类型为条件到spring容器(beans.xml)中去查找bean的节点的类型是UserDao类型对象 * 找到以后,获取该节点对应的对象, 把该对象作为实参传递给该setUserDao(IUserDao userDao)方法的形参 * 如果指定name属性 只能按名称装配 * 获取name属性的值 * 以该值为条件到spring容器(beans.xml)中去查找bean的id节点的值是userDao的对象 * 找到以后,获取该节点对应的对象, 把该对象作为实参传递给该setUserDao(IUserDao userDao)的形参. * 如果不存在该名称,抛出异常 * */ @Resource(name="userDao") public void setUserDao(IUserDao userDao) { this.userDao = userDao; } public void saveUser() { System.out.println("执行UserServiceImpl类的saveUser()的方法!"); userDao.save(); } } 总结:@Resource先会按照名称到spring容器中查找,如果查找不到,就回退按照类型匹配,如果再没有匹配到,就会抛出异常 如果在开发的时候,建议大家都是用@Resource(name=”userDao”),此时只能够按照名称匹配 使用注解完成值的注入 案例:在UserServiceImpl中定义2个属性,使用@Value 15:使用注解进行初始化和销毁 16:使用注解的方式定义组件 作用:在开发时对项目的每一层进行划分 (1)在spring容器中定义: (2)在Dao层代码上定义: /** * @Repository标注在类的上面 * 相当于spring容器中定义: * * 此时如果没有为@Repository指定value属性的名称,此时就是按照类的名称作为spring容器中bean节点的id,并且首字母要小些 * * @Repository(value="userDao") * 相当于spring容器中定义: * */ @Repository(value="userDao") public class UserDaoImpl implements IUserDao { public void save() { System.out.println("执行UserDaoImpl类的save()方法!"); } } (3)在Service层定义: /** * @Service标注在类的上面 * 相当于spring容器中定义: * * 此时如果没有为@@Service指定value属性的名称,此时就是按照类的名称作为spring容器中bean节点的id,并且首字母要小些 * * @Service(userService) * 相当于spring容器中定义: * * * 再添加@Resource(name="userDao") * 此时表示 * * * */ @Service("userService") public class UserServiceImpl implements IUserService { @Resource(name="userDao") private IUserDao userDao; public void saveUser() { System.out.println("执行UserServiceImpl类的saveUser()的方法!"); userDao.save(); } } * 功能介绍 @Service用于标注业务层组件、 @Controller用于标注控制层组件(如struts中的action)、 @Repository用于标注数据访问组件,即DAO组件。 而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。 二:spring第二天 1:面向切面的编程(aop) Spring提供2个代理模式,一个是jdk代理,另一个cglib代理 1.若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。 2.若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。 注意:开发时尽量使用接口的编程, (1)对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统。 (2)标记为final的方法不能够被通知。spring是为目标类产生子类。任何需要 被通知的方法都被复写,将通知织入。final方法是不允许重写的。 (3) spring只支持方法连接点,不支持属性的连接点 2:jdk代理 (1)创建JDKProxy类(使用该类创建代理对象) //使用jdk代理的方式,创建代理对象 public class JDKProxy implements InvocationHandler { //目标对象 private Object targetObject; //使用目标对象创建一个代理对象 public Object createProxyWithTarget(Object targetObject) { this.targetObject = targetObject; //使用目标对象创建代理对象 /* * 第一个参数设置代码使用的类加载器,一般采用跟目标类相同的类加载器 * 第二个参数设置代理类实现的接口,跟目标类使用相同的接口 * 第三个参数设置回调对象,当代理对象的方法被调用时,会调用该参数指定对象的invoke方法 */ return Proxy.newProxyInstance( this.targetObject.getClass().getClassLoader(), this.targetObject.getClass().getInterfaces(), this); } //访问目标对象,要先执行invok的方法 /* * 参数: * Object proxy:代理对象 * Method method:通过Method对象可以访问到目标对象中的方法 * Object[] args:传递给目标对象方法的参数 * 返回值: * Object,表示目标对象方法的返回值 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().contains("save") || method.getName().contains("update")){ security(); System.out.println("代理对象实例:"+proxy.getClass()); System.out.println("目标对象方法的名称:"+method.getName()); if(args!=null && args.length>0){ for(Object o :args){ System.out.println("参数:"+o); } } } //访问目标对象(传递2个参数,参数1:目标对象,参数2:目标对象接收的参数),返回值Object,指的是目标对象方法的返回值 Object returnValue = method.invoke(this.targetObject, args); return returnValue; } //安全校验的方法 public void security(){ System.out.println("开始进行安全校验!!!"); } } (2)客户端App代码调用 //使用代理对象访问目标对象 //需要目标对象 IUserService userService = new UserServiceImpl(); //使用目标对象,创建一个代理对象(代理对象与目标对象实现相同的接口) JDKProxy jdkProxy = new JDKProxy(); IUserService proxyUserService = (IUserService) jdkProxy.createProxyWithTarget(userService); proxyUserService.saveUser("超级强", "123"); proxyUserService.updateUser("超级强", "123"); proxyUserService.deleteUser("超级强"); String str = proxyUserService.findUser(); System.out.println("str:"+str); (3)使用JDK代理总结: * Jdk代理将产生一个代理类:$Proxy0 * jdk代理要求代理类和目标对象要实现同一个接口,也就是说jdk代理是对接口的操作 3:spring提供AOP中的概念 Target(目标对象):需要访问的真实主题就是目标对象,本例:UserServiceImpl类 Proxy(代理对象):在访问目标对象方法之前或者之后,先要执行代理对象的方法,本例:JDKProxy类 joinpoint(连接点):目标对象中定义的所有方法,该方法就是连接点,本例:saveUser(),updateUser(),deleteUser(),findUser() Pointcut(切入点):只对目标对象中的某些方法进行拦截,被拦截的方法就是切入点,本例:saveUser(),updateUser() Advice(通知):通知通常指的是方法,切入点需要做的事情,本例:security(); Aspect(切面): 通知是一个方法,需要放置在类中,该类就是切面,本例:JDKProxy 总结:切面指类,通知指方法,切入点、连接点都是方法 4:Cglib代理 (1)在CglibProxy类中定义: //使用jdk代理的方式,创建代理对象 /**Proxy(代理对象):在访问目标对象方法之前或者之后,先要执行代理对象的方法,本例:JDKProxy类*/ /**Aspect(切面): 通知是一个方法,需要放置在类中,该类就是切面,本例:JDKProxy*/ public class CglibProxy implements MethodInterceptor { //目标对象 private Object targetObject; //使用目标对象创建一个代理对象 /**Weaving(织入):是指把切面应用到目标对象来创建新的代理对象的过程.切面在指定的连接点织入到目标对象*/ public Object createProxyWithTarget(Object targetObject) { this.targetObject = targetObject; //使用Cglib创建代理对象 Enhancer enhancer = new Enhancer(); //使用类加载器 enhancer.setClassLoader(this.targetObject.getClass().getClassLoader()); //使用目标对象调用的类 enhancer.setSuperclass(this.targetObject.getClass()); //调用回调对象,每次访问目标对象方法的时候,先会执行intercept的方法 enhancer.setCallback(this); //创建代理对象 return enhancer.create(); } //访问目标对象,要先执行intercept的方法 /* * 参数: * Object proxy:代理对象 * Method method:通过Method对象可以访问到目标对象中的方法 * Object[] args:传递给目标对象方法的参数 * MethodProxy methodProxy:访问代理对象的方法,获取签名对象用来获取 * 返回值: * Object,表示目标对象方法的返回值 */ public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { /**Pointcut(切入点):只对目标对象中的某些方法进行拦截,被拦截的方法就是切入点,本例:saveUser(),updateUser()*/ if(method.getName().contains("save") || method.getName().contains("update")){ security(); System.out.println("代理对象实例:"+proxy.getClass()); System.out.println("目标对象方法的名称:"+method.getName()); if(args!=null && args.length>0){ for(Object o :args){ System.out.println("参数:"+o); } } //使用methodProxy Signature signature = methodProxy.getSignature(); System.out.println(signature.getName()+" "+signature.getArgumentTypes()+" "+signature.getReturnType()); } //访问目标对象(传递2个参数,参数1:目标对象,参数2:目标对象接收的参数),返回值Object,指的是目标对象方法的返回值 Object returnValue = method.invoke(this.targetObject, args); return returnValue; } //安全校验的方法 /**Advice(通知):通知通常指的是方法,切入点需要做的事情,本例:security();*/ public void security(){ System.out.println("开始进行安全校验!!!"); } } (2)测试类 public class App { public static void main(String[] args) { //直接访问目标对象 // IUserService userService = new UserServiceImpl(); // userService.saveUser("超级强", "123"); // userService.updateUser("超级强", "123"); // userService.deleteUser("超级强"); // String str = userService.findUser(); // System.out.println("str:"+str); //使用代理对象访问目标对象 //需要目标对象 UserServiceImpl userService = new UserServiceImpl(); //使用目标对象,创建一个代理对象(代理对象与目标对象实现相同的接口) CglibProxy cglibProxy = new CglibProxy(); UserServiceImpl proxyUserService = (UserServiceImpl) cglibProxy.createProxyWithTarget(userService); proxyUserService.saveUser("超级强", "123"); proxyUserService.updateUser("超级强", "123"); proxyUserService.deleteUser("超级强"); String str = proxyUserService.findUser(); System.out.println("str:"+str); } } (3)总结: * Cglib代理,产生的代理类UserServiceImpl$$EnhancerByCGLIB$$88a149fc * Cglib代理,操作目标对象的类,而不是接口 5:spring的aop编程(XML方式) 进行AOP编程,需要引入的jar包 包含5种通知类型 (1)前置通知 1)类中Security中定义切面和通知 /**切面*/ public class Security { /**通知*/ /** * 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 ( * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。 * JoinPoint 接口提供了一系列有用的方法, * 比如 getArgs()(返回方法参数)、 * getThis()(返回代理对象)、 * getTarget()(返回目标)、 * getSignature()(返回正在被通知的方法相关信息)、 * toString() (打印出正在被通知的方法的有用信息)。 */ public void checkSecurity(JoinPoint joinPoint){ System.out.println("正在执行验证..."); Object [] args = joinPoint.getArgs(); if(args!=null && args.length>0){ for(Object o:args){ System.out.println("参数:"+o); } } System.out.println("代理对象:"+joinPoint.getThis().getClass()); System.out.println("目标对象:"+joinPoint.getTarget().getClass()); System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName()); System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString()); if(true){ throw new RuntimeException("抛出运行时异常!"); } } } 2)在spring容器中的写法: 3)切入点表达式的写法 4)前置通知的作用: 在访问目标对象方法之前先会执行通知的方法,如果在项目开发中,如权限校验的时候,在访问目标对象方法之前,先进行校验,如果校验成功,访问目标对象方法,如果不成功,此时不会访问。 (2)后置通知 1)在切面中定义 /**切面*/ public class Security { /**通知*/ /** * 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 ( * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。 * JoinPoint 接口提供了一系列有用的方法, * 比如 getArgs()(返回方法参数)、 * getThis()(返回代理对象)、 * getTarget()(返回目标)、 * getSignature()(返回正在被通知的方法相关信息)、 * toString() (打印出正在被通知的方法的有用信息)。 */ public void checkSecurity(JoinPoint joinPoint,Object returnValue){ System.out.println("正在执行验证..."); Object [] args = joinPoint.getArgs(); if(args!=null && args.length>0){ for(Object o:args){ System.out.println("参数:"+o); } } System.out.println("代理对象:"+joinPoint.getThis().getClass()); System.out.println("目标对象:"+joinPoint.getTarget().getClass()); System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName()); System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString()); System.out.println("目标对象方法的返回值:"+returnValue); } } 2)在spring容器中定义: 3)后置通知在项目中的定义 * 关联流 * 日志处理,当在Service或者在Dao中向一个表中新增数据,同时再向日志表新增一条数据,可以将新增日志表的数据操作放置到后置通知执行 (3)异常通知 1)在通知的方法中定义: /**切面*/ public class Security { /**通知*/ /** * 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 ( * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。 * JoinPoint 接口提供了一系列有用的方法, * 比如 getArgs()(返回方法参数)、 * getThis()(返回代理对象)、 * getTarget()(返回目标)、 * getSignature()(返回正在被通知的方法相关信息)、 * toString() (打印出正在被通知的方法的有用信息)。 */ public void checkSecurity(JoinPoint joinPoint,Throwable throwingValue){ System.out.println("正在执行验证..."); Object [] args = joinPoint.getArgs(); if(args!=null && args.length>0){ for(Object o:args){ System.out.println("参数:"+o); } } System.out.println("代理对象:"+joinPoint.getThis().getClass()); System.out.println("目标对象:"+joinPoint.getTarget().getClass()); System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName()); System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString()); System.out.println("目标对象方法抛出的异常是:"+throwingValue); } } 2)在spring的容器中定义: (4)最终通知 1)在通知中定义: /**切面*/ public class Security { /**通知*/ /** * 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 ( * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。 * JoinPoint 接口提供了一系列有用的方法, * 比如 getArgs()(返回方法参数)、 * getThis()(返回代理对象)、 * getTarget()(返回目标)、 * getSignature()(返回正在被通知的方法相关信息)、 * toString() (打印出正在被通知的方法的有用信息)。 */ public void checkSecurity(JoinPoint joinPoint){ System.out.println("正在执行验证..."); Object [] args = joinPoint.getArgs(); if(args!=null && args.length>0){ for(Object o:args){ System.out.println("参数:"+o); } } System.out.println("代理对象:"+joinPoint.getThis().getClass()); System.out.println("目标对象:"+joinPoint.getTarget().getClass()); System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName()); System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString()); } } 2)在spring容器中定义: (5)环绕通知 1)在通知的方法中定义: /**切面*/ public class Security { /**通知*/ /** * 普通通知类型: * 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 ( * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。 * JoinPoint 接口提供了一系列有用的方法, * 比如 getArgs()(返回方法参数)、 * getThis()(返回代理对象)、 * getTarget()(返回目标)、 * getSignature()(返回正在被通知的方法相关信息)、 * toString() (打印出正在被通知的方法的有用信息)。 * * 环绕通知类型 * 通知的第一个参数必须是 ProceedingJoinPoint类型。 * 在通知体内,调用 ProceedingJoinPoint的proceed()方法会导致 后台的连接点方法执行。 * proceed 方法也可能会被调用并且传入一个 Object[]对象-该数组中的值将被作为方法执行时的参数。 * * 环绕通知将通知的方法的返回值要定义成Object类型,只有这样才能将目标对象方法的返回值,传递给客户端 */ public Object checkSecurity(ProceedingJoinPoint joinPoint){ //如果调用joinPoint.proceed();方法放置在通知的最前面,此时就相当于后置通知 Object value = null; try { value = joinPoint.proceed(); } catch (Throwable e) { e.printStackTrace(); } System.out.println("正在执行验证..."); Object [] args = joinPoint.getArgs(); if(args!=null && args.length>0){ for(Object o:args){ System.out.println("参数:"+o); } } System.out.println("代理对象:"+joinPoint.getThis().getClass()); System.out.println("目标对象:"+joinPoint.getTarget().getClass()); System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName()); System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString()); // //如果调用joinPoint.proceed();方法放置在通知的最后,此时就相当于前置通知 // Object value = null; // try { // value = joinPoint.proceed(); // } catch (Throwable e) { // e.printStackTrace(); // } return value; } } 2)在spring容器中定义: 6:spring的aop编程(注解方式) (1)前置通知 1)在通知的方法中定义: /**切面*/ @Aspect //表示在spring容器中定义: public class Security { /** * 声明定义目标对象方法的切入点(指的是方法) * 要求:1、方法的修饰符任意 * 2、要求方法没有返回值 * 3、方法的名称任意(一个类中,不允许出现同名方法,表明的切入点的id惟一) * 4、方法没有参数 * 5、方法体为空 */ //相当于spring容器中定义: @Pointcut(value="execution(* cn.itcast.f_aspectJ.a_before.UserServiceImpl.saveUser(..))") public void save(){}; @Pointcut(value="execution(* cn.itcast.f_aspectJ.a_before.UserServiceImpl.updateUser(..))") public void update(){}; /**通知*/ /** * 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 ( * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。 * JoinPoint 接口提供了一系列有用的方法, * 比如 getArgs()(返回方法参数)、 * getThis()(返回代理对象)、 * getTarget()(返回目标)、 * getSignature()(返回正在被通知的方法相关信息)、 * toString() (打印出正在被通知的方法的有用信息)。 */ //相当于spring容器中定义: @Before(value="save() || update()") public void checkSecurity(JoinPoint joinPoint){ System.out.println("正在执行验证..."); Object [] args = joinPoint.getArgs(); if(args!=null && args.length>0){ for(Object o:args){ System.out.println("参数:"+o); } } System.out.println("代理对象:"+joinPoint.getThis().getClass()); System.out.println("目标对象:"+joinPoint.getTarget().getClass()); System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName()); System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString()); // if(true){ // throw new RuntimeException("抛出运行时异常!"); // } } } 2)在spring容器中定义: (2)后置通知 1)在通知的方法中定义: /**切面*/ @Aspect //表示在spring容器中定义: public class Security { /** * 声明定义目标对象方法的切入点(指的是方法) * 要求:1、方法的修饰符任意 * 2、要求方法没有返回值 * 3、方法的名称任意(一个类中,不允许出现同名方法,表明的切入点的id惟一) * 4、方法没有参数 * 5、方法体为空 */ //相当于spring容器中定义: @Pointcut(value="execution(* cn.itcast.f_aspectJ.b_afterReturning.UserServiceImpl.saveUser(..))") public void save(){}; @Pointcut(value="execution(* cn.itcast.f_aspectJ.b_afterReturning.UserServiceImpl.findUser(..))") public void find(){}; /**通知*/ /** * 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 ( * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。 * JoinPoint 接口提供了一系列有用的方法, * 比如 getArgs()(返回方法参数)、 * getThis()(返回代理对象)、 * getTarget()(返回目标)、 * getSignature()(返回正在被通知的方法相关信息)、 * toString() (打印出正在被通知的方法的有用信息)。 */ //相当于spring容器中定义: @AfterReturning(value="save() || find()",returning="returnValue") public void checkSecurity(JoinPoint joinPoint,Object returnValue){ System.out.println("正在执行验证..."); Object [] args = joinPoint.getArgs(); if(args!=null && args.length>0){ for(Object o:args){ System.out.println("参数:"+o); } } System.out.println("代理对象:"+joinPoint.getThis().getClass()); System.out.println("目标对象:"+joinPoint.getTarget().getClass()); System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName()); System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString()); System.out.println("目标对象方法的返回值:"+returnValue); // if(true){ // throw new RuntimeException("抛出运行时异常!"); // } } } 2)在spring容器中定义: (3)异常通知 /**切面*/ @Aspect //表示在spring容器中定义: public class Security { /** * 声明定义目标对象方法的切入点(指的是方法) * 要求:1、方法的修饰符任意 * 2、要求方法没有返回值 * 3、方法的名称任意(一个类中,不允许出现同名方法,表明的切入点的id惟一) * 4、方法没有参数 * 5、方法体为空 */ //相当于spring容器中定义: @Pointcut(value="execution(* cn.itcast.f_aspectJ.c_afterThrowing.UserServiceImpl.saveUser(..))") public void save(){}; @Pointcut(value="execution(* cn.itcast.f_aspectJ.c_afterThrowing.UserServiceImpl.findUser(..))") public void find(){}; /**通知*/ /** * 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 ( * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。 * JoinPoint 接口提供了一系列有用的方法, * 比如 getArgs()(返回方法参数)、 * getThis()(返回代理对象)、 * getTarget()(返回目标)、 * getSignature()(返回正在被通知的方法相关信息)、 * toString() (打印出正在被通知的方法的有用信息)。 */ //相当于spring容器中定义: @AfterThrowing(value="save() || find()",throwing="throwingValue") public void checkSecurity(JoinPoint joinPoint,Throwable throwingValue){ System.out.println("正在执行验证..."); Object [] args = joinPoint.getArgs(); if(args!=null && args.length>0){ for(Object o:args){ System.out.println("参数:"+o); } } System.out.println("代理对象:"+joinPoint.getThis().getClass()); System.out.println("目标对象:"+joinPoint.getTarget().getClass()); System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName()); System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString()); System.out.println("目标对象抛出的异常:"+throwingValue); } } 2)spring容器定义: (4)最终通知 1)在通知的类中定义: /**切面*/ @Aspect //表示在spring容器中定义: public class Security { /** * 声明定义目标对象方法的切入点(指的是方法) * 要求:1、方法的修饰符任意 * 2、要求方法没有返回值 * 3、方法的名称任意(一个类中,不允许出现同名方法,表明的切入点的id惟一) * 4、方法没有参数 * 5、方法体为空 */ //相当于spring容器中定义: @Pointcut(value="execution(* cn.itcast.f_aspectJ.d_after.UserServiceImpl.saveUser(..))") public void save(){}; @Pointcut(value="execution(* cn.itcast.f_aspectJ.d_after.UserServiceImpl.findUser(..))") public void find(){}; /**通知*/ /** * 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 ( * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。 * JoinPoint 接口提供了一系列有用的方法, * 比如 getArgs()(返回方法参数)、 * getThis()(返回代理对象)、 * getTarget()(返回目标)、 * getSignature()(返回正在被通知的方法相关信息)、 * toString() (打印出正在被通知的方法的有用信息)。 */ //相当于spring容器中定义: @After(value="save() || find()") public void checkSecurity(JoinPoint joinPoint){ System.out.println("正在执行验证..."); Object [] args = joinPoint.getArgs(); if(args!=null && args.length>0){ for(Object o:args){ System.out.println("参数:"+o); } } System.out.println("代理对象:"+joinPoint.getThis().getClass()); System.out.println("目标对象:"+joinPoint.getTarget().getClass()); System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName()); System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString()); } } 2)在spring容器中定义: (5)环绕通知 1)在通知的方法中定义: @Aspect //表示在spring容器中定义: public class Security { /** * 声明定义目标对象方法的切入点(指的是方法) * 要求:1、方法的修饰符任意 * 2、要求方法没有返回值 * 3、方法的名称任意(一个类中,不允许出现同名方法,表明的切入点的id惟一) * 4、方法没有参数 * 5、方法体为空 */ //相当于spring容器中定义: @Pointcut(value="execution(* cn.itcast.f_aspectJ.e_around.UserServiceImpl.saveUser(..))") public void save(){}; @Pointcut(value="execution(* cn.itcast.f_aspectJ.e_around.UserServiceImpl.findUser(..))") public void find(){}; /**通知*/ /** * 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 ( * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。 * JoinPoint 接口提供了一系列有用的方法, * 比如 getArgs()(返回方法参数)、 * getThis()(返回代理对象)、 * getTarget()(返回目标)、 * getSignature()(返回正在被通知的方法相关信息)、 * toString() (打印出正在被通知的方法的有用信息)。 */ //相当于spring容器中定义: @Around(value="save() || find()") public Object checkSecurity(ProceedingJoinPoint joinPoint){ System.out.println("正在执行验证..."); Object [] args = joinPoint.getArgs(); if(args!=null && args.length>0){ for(Object o:args){ System.out.println("参数:"+o); } } System.out.println("代理对象:"+joinPoint.getThis().getClass()); System.out.println("目标对象:"+joinPoint.getTarget().getClass()); System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName()); System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString()); //调用proceed,放置到最后,此时相当于前置通知 Object value = null; try { value = joinPoint.proceed(); } catch (Throwable e) { e.printStackTrace(); } return value; } } 2)在spring容器中的写法: 总结: 1:spring的5种类型,目的在访问目标对象方法之前或者之后,先执行通知定义的方法,这种编程就是aop(面向切面的编程) * 应用(声明式事务处理) 2:对于环绕通知来说: Object value = joinPoint.proceed();就类似于jdk代理和cglib代理的Object returnValue = method.invoke(this.targetObject, args);,此时效果是一样的 7:征服数据库 (1)配置DBCP连接池 在spring容器中定义: (2)测试DBCP连接池 package cn.itcast.a_datasource; import javax.sql.DataSource; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { @Test public void testDataSource(){ ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/a_datasource/beans.xml"); DataSource dataSource = (DataSource)ac.getBean("dataSource"); System.out.println("dataSource:"+dataSource); } } (2)使用DBCP连接池,使用JDBC操作数据库,CRUD 1)在spring容器中定义: 2)在AccountDaoImpl类中定义 package cn.itcast.b_crud; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; public class AccountDaoImpl implements IAccountDao { private JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public void saveAccount(Account account) { String sql = "insert into account(accountid,balance) values (?,?)"; Object [] args = {account.getAccountid(),account.getBalance()}; //加和不加没有影响,表示指定参数的类型 int [] argTypes = {java.sql.Types.VARCHAR,java.sql.Types.DOUBLE}; jdbcTemplate.update(sql, args,argTypes); } @Override public void updateAccount(Account account) { String sql = "update account set balance=? where accountid=?"; Object [] args = {account.getBalance(),account.getAccountid()}; jdbcTemplate.update(sql, args); } @Override public void deleteAccountById(String accountid) { String sql = "delete from account where accountid=?"; Object [] args = {accountid}; jdbcTemplate.update(sql, args); } @Override public Account findAccountById(String accountid) { String sql = "select accountid,balance from account where accountid=?"; Object [] args = {accountid}; /** * RowMapper:查询结果的每一行的数据,映射成指定的对象 */ RowMapper rowMapper = new RowMapper() { /** * 目的:将jdbc每一行的放回结果(封装到ResultSet中),放置到指定的返回对象(封装到Account对象)中 * ResultSet rs:结果集对象 * int rowNum:每一行的索引,0表示第一行的索引 */ @Override public Account mapRow(ResultSet rs, int rowNum) throws SQLException { System.out.println(rs.getString("accountid")+" "+rs.getDouble("balance")+" "+rowNum); Account account = new Account(); account.setAccountid(rs.getString("accountid")); account.setBalance(rs.getDouble("balance")); return account; } }; Account account = jdbcTemplate.queryForObject(sql, args, rowMapper); return account; } @Override public List findAccountList() { String sql = "select accountid,balance from account"; /** * RowMapper:查询结果的每一行的数据,映射成指定的对象 */ RowMapper rowMapper = new RowMapper() { /** * 目的:将jdbc每一行的放回结果(封装到ResultSet中),放置到指定的返回对象(封装到Account对象)中 * ResultSet rs:结果集对象 * int rowNum:每一行的索引,0表示第一行的索引 */ @Override public Account mapRow(ResultSet rs, int rowNum) throws SQLException { System.out.println(rs.getString("accountid")+" "+rs.getDouble("balance")+" "+rowNum); Account account = new Account(); account.setAccountid(rs.getString("accountid")); account.setBalance(rs.getDouble("balance")); return account; } }; List list = jdbcTemplate.query(sql, rowMapper); return list; } } 3)测试类的代码(App类) public class App { //加载spring容器,由容器创建Dao的对象 ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/b_crud/beans.xml"); IAccountDao accountDao = (IAccountDao)ac.getBean("accountDao"); @Test public void testDataSource(){ ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/b_crud/beans.xml"); DataSource dataSource = (DataSource)ac.getBean("dataSource"); System.out.println("dataSource:"+dataSource); } /**新增账号*/ @Test public void saveAccount(){ Account account = new Account(); account.setAccountid("8888"); account.setBalance(300d); accountDao.saveAccount(account); } /**更新账号*/ @Test public void updateAccount(){ Account account = new Account(); account.setAccountid("6666"); account.setBalance(200d); accountDao.updateAccount(account); } /**删除账号*/ @Test public void deleteAccount(){ //通过ID删除 String accountid = "8888"; accountDao.deleteAccountById(accountid); } /**查询(指定ID,查询对象,Account)*/ @Test public void findAccountById(){ String accountid = "8888"; Account account = accountDao.findAccountById(accountid); System.out.println(account.getAccountid()+" "+account.getBalance()); } /**查询(查询所有),返回List*/ @Test public void findAccountList(){ List list = accountDao.findAccountList(); if(list!=null && list.size()>0){ for(Account account:list){ System.out.println(account.getAccountid()+" "+account.getBalance()); } } } } 问题:每次使用junit测试类的时候(增删改查),调用Dao的时候,都需要加载spring容器,即代码: //加载spring容器,由容器创建Dao的对象 ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/b_crud/beans.xml"); IAccountDao accountDao = (IAccountDao)ac.getBean("accountDao"); 能否使用spring整合junit呢?回答,没问题。 (3)Spring整合junit,完成测试 1:引入jar包 l 导入Spring test测试jar包(使用junit4.5及以上版本测试) • org.springframework.test-3.0.2.RELEASE.jar 2:以上App代码可以修改为: @RunWith(value=SpringJUnit4ClassRunner.class) @ContextConfiguration(value="classpath:cn/itcast/b_crud/beans.xml") public class App { @Resource(name="accountDao") IAccountDao accountDao; @Test public void testDataSource(){ ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/b_crud/beans.xml"); DataSource dataSource = (DataSource)ac.getBean("dataSource"); System.out.println("dataSource:"+dataSource); } /**新增账号*/ @Test public void saveAccount(){ Account account = new Account(); account.setAccountid("8888"); account.setBalance(300d); accountDao.saveAccount(account); } /**更新账号*/ @Test public void updateAccount(){ Account account = new Account(); account.setAccountid("6666"); account.setBalance(200d); accountDao.updateAccount(account); } /**删除账号*/ @Test public void deleteAccount(){ //通过ID删除 String accountid = "8888"; accountDao.deleteAccountById(accountid); } /**查询(指定ID,查询对象,Account)*/ @Test public void findAccountById(){ String accountid = "8888"; Account account = accountDao.findAccountById(accountid); System.out.println(account.getAccountid()+" "+account.getBalance()); } /**查询(查询所有),返回List*/ @Test public void findAccountList(){ List list = accountDao.findAccountList(); if(list!=null && list.size()>0){ for(Account account:list){ System.out.println(account.getAccountid()+" "+account.getBalance()); } } } } 3:注意使用spring与junit完成整合,必须要使用junit4.5以上的版本,否则不可以 我们使用com.springsource.org.junit-4.7.0.jar 三:spring第三天 事务处理: * 声明式事务管理(*****) 如果你并不需要细粒度的事务控制,你可以使用声明式事务,在Spring中,你只需要在Spring配置文件中做一些配置,即可将操作纳入到事务管理中,解除了和代码的耦合, 这是对应用代码影响最小的选择,从这一点再次验证了Spring关于AOP的概念。当你不需要事务管理的时候,可以直接从Spring配置文件中移除该设置 * 事务管理器: 1:传播行为 例如一个操作: 声明对应的传播行为 总结: REQUIRED(默认值):也是项目应用最多的传播行为,因为他表示如果业务方法存在一个事务中,则直接使用,如果允许中不存在事务,则开启一个新的事务。 2:隔离级别 总结: 开发时使用DEFAULT(默认值),表示数据库采用那么隔离级别,我们就使用哪种隔离级别 3:声明式事务处理(XML) (1)在spring容器中定义: (2)Spring事务控制的原理: 在控制层访问业务的方法时,实质上产生一个代理对象$Proxy,在代理对象中对业务层的方法用事务进行控制,这样就将业务层的方法用一个事务做了控制 实质上代理产生的代码(因为在容器中进行配置):这个配置再次验证spring关于aop的思想 (3)注意:如果在Dao中或者在Service抛出异常 · 任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚 * 在开发过程中,要想对事务进行控制,如果需要抛出异常的时候,需要在Service或者Dao层的方法中抛出运行时异常 * 自定义异常(也可以进行事务控制) 在cn.itcast.exception包中定义2个Service异常和Dao异常 public class DaoException extends Exception { public DaoException() { super(); } public DaoException(String message) { super(message); } } public class ServiceException extends Exception { public ServiceException() { super(); } public ServiceException(String message) { super(message); } } 在spring容器中定义: 其中rollback-for是将被触发进行回滚的 Exception(s);以逗号分开。 4:声明式事务处理(注解) (1)在配置文件中的写法 (2)在业务层的类中定义: /** * @Transcational注解 * * 放置到类的上面,默认对类中的所有方法都有效,而且默认是可写的操作 * * 放置到方法的上面:此时方法级别的事务,会覆盖类级别的事务 * * 注解方式总结:在业务层的类上面定义:@Transactional(readOnly=true) * 在业务层类的可写的方法(新增、删除、修改)上面定义:@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,readOnly=false) * 查询的方法无需定义@Transactional * * 相当于spring容器中定义: * * */ @Service("accountService") @Transactional(readOnly=true) public class AccountServiceImpl implements IAccountService { @Resource(name="inAccountDao") private IInAccountDao inAccountDao; @Resource(name="accountDao") private IAccountDao accountDao; /**向8888的账号存款200元钱*/ @Override @Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,readOnly=false) public void saveAccount(InAccount inAccount) { //1:向存款信息表中的8888账号存款200元,存放一条记录 inAccountDao.save(inAccount); //2:使用8888的账号查询账号信息表,获取8888账号对应的余额 String accountid = inAccount.getAccountid(); Account account = accountDao.findAccountById(accountid); //3:更新8888账号的余额(余额=存款的金额+新增的金额) Double balance = account.getBalance()+inAccount.getInbalance(); account.setBalance(balance); //4:更新账号信息表,对8888的账号,将余额更新最新的余额 accountDao.updateAccount(account); } } (3)总结: 1)方法级别的事务,会覆盖类级别的事务 2)注解放置到类的上面,默认对类中的所有方法都有效,而且默认是可写的操作 3) 在业务层的类上面定义:@Transactional(readOnly=true) * 在业务层类的可写的方法(新增、删除、修改)上面定义:@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,readOnly=false) * 查询的方法无需定义@Transactional 5:struts2+hibernate3+spring3整合 (1)创建web工程,导入jar包 Jar包在资料【资料\挑选出的jar包\s2s3h3\全】 (2)创建数据库表(完成对person表的新增操作) 表结构如下 create table person ( id int not null auto_increment primary key, name varchar(255) ) (3)持久层(hibernate) 1)在cn.itcast.ssh.domain中创建类Person public class Person implements java.io.Serializable{ private Integer id; //id private String name; //名称 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } 2)在在cn.itcast.ssh.domain中创建对应Person类的映射文件Person.hbm.xml 3)在src下创建hibernate.cfg.xml com.mysql.jdbc.Driver jdbc:mysql://localhost:3306/testspring?useUnicode=true&characterEncoding=utf8 root root org.hibernate.dialect.MySQL5Dialect update true 4)在src下导入log4j.properties文件,使用junit进行测试 package test; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.junit.Test; import cn.itcast.ssh.domain.Person; public class TestHibernate { @Test public void save(){ Configuration configuration = new Configuration(); //加载hibernate.cfg.xml和Person.hbm.xml configuration.configure(); SessionFactory sf = configuration.buildSessionFactory(); Session s = sf.openSession(); Transaction tr = s.beginTransaction(); Person p = new Person(); p.setName("西门庆"); s.save(p); tr.commit(); s.close(); } } (4)控制层(struts2) 1)导入jsp页面:person.jsp和success.jsp <%@ page language="java" contentType="text/html; charset=utf-8"%> <%@ taglib uri="/struts-tags" prefix="s"%> person.jsp
姓名:
         

2)在cn.itcast.ssh.web.action包中创建PersonAction类 @SuppressWarnings("serial") public class PersonAction extends ActionSupport implements ModelDriven { Person person = new Person(); public Person getModel() { return person; } /**保存*/ public String save(){ System.out.println("执行Action类!"+person.getName()); return "success"; } } 3)在src下创建struts.xml文件 /success.jsp 4)在web容器中配置(过滤器): struts2 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter struts2 /* person.jsp 5)使用页面访问路径:http://localhost:8888/itcast1209_d_springSSH,进行测试 (5)业务层+Dao层(spring整合hibernate) 1)在cn.itcast.ssh.service包创建接口和实现类 public interface IPersonService { void savePerson(Person person); } public class PersonServiceImpl implements IPersonService { private IPersonDao personDao; public void setPersonDao(IPersonDao personDao) { this.personDao = personDao; } /**保存*/ public void savePerson(Person person) { personDao.save(person); } } 2)在cn.itcast.ssh.dao包中定义接口和实现类 public interface IPersonDao { void save(Person person); } public class PersonDaoImpl implements IPersonDao { private HibernateTemplate hibernateTemplate; public void setHibernateTemplate(HibernateTemplate hibernateTemplate) { this.hibernateTemplate = hibernateTemplate; } /**保存人员信息*/ public void save(final Person person) { //方案一:完成保存 hibernateTemplate.save(person); //方案二:调用底层的Sesison // hibernateTemplate.execute(new HibernateCallback() { // // public Object doInHibernate(Session session) // throws HibernateException, SQLException { // session.save(person); // return null; // } // // }); //方案三: // SessionFactory sf = hibernateTemplate.getSessionFactory(); // Session s = sf.getCurrentSession();//在hibernate.cfg.xml中配置:thread // s.save(person); } } 3)在src下创建spring容器(beans.xml) classpath:hibernate.cfg.xml 4)使用person.jsp完成测试。 总结: 现在访问业务层,实质上是通过代理的方式方法,代理中定义事务处理的相关操作,大大简化了业务层的代码。 同时hibernate操作Dao的时候,也使用了hibernateTemplate,也大大简化了Dao层操作。 同时存在问题:在每次访问Action的方法的时候,每次都会执行 ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml"); IPersonService personService = (IPersonService)ac.getBean("personService"); 此时加载spring容器很慢,而且影响性能,能否在web容器启动的时候,就加载spring容器呢? (6)控制层(spring整合struts2,用来加载spring容器) 1)导入struts2-spring-plugin-2.3.3.jar 在jar包中定义:struts-plugin.xml中定义: ,这个配置在struts的default.properties文件中是被注释,一旦解开,要求在web容器中,要使用监听,监听的作用就是在web容器启动的时候,自动加载spring容器。 2)在web容器添加:当web容器启动的时候,自动加载spring容器 contextConfigLocation classpath:beans.xml org.springframework.web.context.ContextLoaderListener 3)在spring的配置文件中定义: 4)修改struts.xml文件的 /success.jsp 此时class的属性的名称对应spring容器中定义的Action节点的id属性名称。 5)修改PersonAction的类 private IPersonService personService; public void setPersonService(IPersonService personService) { this.personService = personService; } public String save(){ personService.savePerson(person); return "success"; } 6:struts2+hibernate3+spring3整合(注解) 1)在spring容器中定义: classpath:hibernate.cfg.xml 2)Dao层的代码: @Repository("personDao") public class PersonDaoImpl implements IPersonDao { @Resource(name="hibernateTemplate") private HibernateTemplate hibernateTemplate; /**保存人员信息*/ public void save(final Person person) { //方案一:完成保存 hibernateTemplate.save(person); //方案二:调用底层的Sesison // hibernateTemplate.execute(new HibernateCallback() { // // public Object doInHibernate(Session session) // throws HibernateException, SQLException { // session.save(person); // return null; // } // // }); //方案三: // SessionFactory sf = hibernateTemplate.getSessionFactory(); // Session s = sf.getCurrentSession();//在hibernate.cfg.xml中配置:thread // s.save(person); } } 3)Service类中的代码 @Service("personService") @Transactional(readOnly=true) public class PersonServiceImpl implements IPersonService { @Resource(name="personDao") private IPersonDao personDao; /**保存*/ @Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,readOnly=false) public void savePerson(Person person) { personDao.save(person); } } 4)Action类的代码: @SuppressWarnings("serial") @Controller("personAction") @Scope(value="prototype")//表示spring创建一个多实例的对象Action public class PersonAction extends ActionSupport implements ModelDriven { @Resource(name="personService") private IPersonService personService; Person person = new Person(); public Person getModel() { return person; } /**保存*/ public String save(){ //System.out.println("执行Action类!"+person.getName()); //加载spring容器 // ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml"); // IPersonService personService = (IPersonService)ac.getBean("personService"); personService.savePerson(person); return "success"; } } 7:struts2+hibernate3+spring3(XML方式分离) Xml方式分离:想实现struts2单独是一个框架,spring单独是一个框架,而不想让struts2与spring整合。 分离方式一: 1) 在spring容器中去掉创建Action的代码 2) 在struts.xml中配置: /success.jsp 3) 在web容器启动的时候,自动加载spring容器 4) 在Action类的代码中定义: /**保存*/ public String save(){ //System.out.println("执行Action类!"+person.getName()); //当web容器已经加载spring容器,此时不需要再加载spring容器,而只需要获取ApplicationContext对象就可以了 WebApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(ServletActionContext.getServletContext()); IPersonService personService = (IPersonService)ac.getBean("personService"); personService.savePerson(person); return "success"; } 分离方式二: 1) 在spring容器中去掉创建Action的代码 2) 在struts.xml中配置: /success.jsp 3) 在web容器启动的时候,自动加载spring容器 4) 在Action类的代码中定义: public class PersonAction extends ActionSupport implements ModelDriven,ApplicationContextAware { ApplicationContext ac = null; Person person = new Person(); public Person getModel() { return person; } /**保存*/ public String save(){ //System.out.println("执行Action类!"+person.getName()); //当web容器已经加载spring容器,此时不需要再加载spring容器,而只需要获取ApplicationContext对象就可以了 // WebApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(ServletActionContext.getServletContext()); IPersonService personService = (IPersonService)ac.getBean("personService"); personService.savePerson(person); return "success"; } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.ac = applicationContext; } } 8:添加c3p0连接池: 1)导入jar包 添加c3p0数据源(spring): spring-framework-3.0.2.RELEASE-dependencies.zip\com.mchange.c3p0\com.springsource.com.mchange.v2.c3p0\0.9.1.2\com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar 2)在spring的配置文件中定义: classpath:hibernate.cfg.xml 3)去掉hibernate.cfg.xml操作数据库的配置,由c3p0完成 9:Quzrtz(spring企业开发调度器) 1)导入jar包 2)spring提供的触发器(频率) 1))在cn.itcast.quartz包下,创建CurDateBean类中定义: /** * 1、创建一个执行任务的类。要做到这一点,你需要从Spring的QuartzJobBean中派生子类 * QuartzJobBean是Quartz中与Java的TimerTask等价的类。 * 它实现了org.quartz.Job接口。executeInternal()方法定义了当预定的时刻来临时应该执行哪些动作 */ public class CurDateBean extends QuartzJobBean { public CurDateBean(){ System.out.println("这是CurDateBean的构造方法!"); } /** * 定时器需要处理的业务方法 * 发送邮件、日志清理、数据库备份 */ @Override protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException { System.out.println("执行Quart定时器定义的任务!"+new Date()); } } 2))在spring的容器中定义: cn.itcast.quartz.CurDateBean 3))在web容器添加监听 contextConfigLocation classpath:beans.xml org.springframework.web.context.ContextLoaderListener 3)spring提供的触发器(定时到某一时刻) 1))在spring容器中的表达式的写法: 此时表达式可以写成: 2))任务调度中使用符号的用法: 一个cron表达式有至少6个(也可能是7个)由空格分隔的时间元素。从左至右,这些元素的定义如下 参数1:秒 0 - 59 参数2:分钟 0-59 参数3:小时 0-23 参数4:月份中的日期 1-31 参数5:月份 1-12或JAN-DEC 参数6:星期中的日期 1-7或SUN-SAT 参数7:年份1970-2099 每一个元素都可以显式地规定一个值(如6),一个区间(如9-12),一个列表(如9,11,13)或一个通配符(如*)。“月份中的日期”和“星期中的 日期”这两个元素是互斥的,因此应该通过设置一个问号(?)来表明你不想设置的那个字段 位置 时间域名 允许值 允许的特殊字符 1 秒 0-59 , - * / 2 分钟 0-59 , - * / 3 小时 0-23 , - * / 4 日期 1-31 , - * ? / L W C 5 月份 1-12 , - * / 6 星期 1-7 , - * ? / L C # 7 年(可选) 空值1970-2099 , - * / Cron表达式的时间字段除允许设置数值外,还可使用一些特殊的字符,提供列表、范围、通配符等功能,细说如下: ●星号(*):可用在所有字段中,表示对应时间域的每一个时刻,例如,*在分钟字段时,表示“每分钟”; ●问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符; ●减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12; ●逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五; ●斜杠(/):x/y表达一个等步长序列,x为起始值,y为增量步长值。如在分钟字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y; 四:spring第四天 1:数据库设计: 2:框架搭建: (1)hibernate层 1)在cn.itcast.store.domain中创建4个对应数据表的4个javabean对象和映射文件: 2)在src下创建hibernate包,在包中定义hibernate.cfg.xml com.mysql.jdbc.Driver jdbc:mysql://localhost:3306/testspring?useUnicode=true&characterEncoding=utf8 root root org.hibernate.dialect.MySQL5Dialect update true thread (2)spring层 1)在src下创建spring的包,在包中定义beans.xml classpath:hibernate/hibernate.cfg.xml (3)struts层 1)在src下创建struts.xml文件: (4)在web容器中定义: contextConfigLocation classpath:spring/beans.xml org.springframework.web.context.ContextLoaderListener struts2 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter struts2 /* index.jsp (5)导入原型中的css,js,jsp等,进行测试 3:项目层次 (1)建立层次关系 (2)对配置文件进行相关操作 1)在struts.xml中使用: 在struts-userinfo.xml中定义: /jsps/main.jsp 2)在beans.xml中使用: 在beans.userinfo.xml中定义: 4:使用HibernateDaoSupport (1)之前的写法在Dao中定义: public class UserInfoDaoImpl implements IUserInfoDao { private HibernateTemplate hibernateTemplate; public void setHibernateTemplate(HibernateTemplate hibernateTemplate) { this.hibernateTemplate = hibernateTemplate; } } (2)现在的写法在Dao中继承类:HibernateDaoSupport 这个类是spring提供与hibernate整合的类,直接将hibernateTemplate封装到该类中,所有在Dao中不需要在配置模板了 此时Dao中定义: public class UserInfoDaoImpl extends HibernateDaoSupport implements IUserInfoDao { } 在spring容器中定义: 或者写成:直接注入sessionFatory工厂,此时在容器中不需要创建模板直接通过HibernateDaoSupport用来使用Sessionfactory工厂创建hibernateTmplate 5:项目中处理延迟检索(懒加载异常) 问题:如果在session关闭之后,再次调用和一个对象关联的属性的时候,由于该属性在session关闭之前没有查询,所有此时报懒加载异常,项目开发中通常在业务层控制session (1)解决方案: 在业务层的方法中 public List findStoreList() { List list = storeDao.findStoreList(); if(list!=null && list.size()>0){ for(Store store:list){ //解决懒加载异常(调用除了Goods对象oid之外的其他属性) store.getGoods().size(); //使用Hibernate Hibernate.initialize(store.getGoods()); } } return list; } (2)解决方案: 在查询Store对象的同时,将Store对象对应的Goods对象的集合查询出来 在Store.hbm.xml中定义: 缺点:只要查询Store对象,同时也将关联集合对象全部查询出来,此时会有性能问题(中小型项目会选择使用hibernate,大型项目一般还是选择jdbc操作) (3)解决方案 使用OpenSessionInViewFilter过滤器,该过滤器由spring提供! 原理图: 在web容器中添加: OpenSessionInViewFilter org.springframework.orm.hibernate3.support.OpenSessionInViewFilter OpenSessionInViewFilter /* 问题: 一切看起来很正确,尤其是在本地开发测试的时候没出现问题,但试想下如果流程中的某一步被阻塞的话,那在这期间connection就一直被占用而不释放。最有可能被阻塞的就是在写Jsp这步,一方面可能是页面内容大,response.write的时间长,另一方面可能是网速慢,服务器与用户间传输时间久。当大量这样的情况出现时,就有连接池连接不足,造成页面假死现象。 Open Session In View是个双刃剑,放在公网上内容多流量大的网站请慎用 6:遍历select标签 返回List,在页面上如何遍历? 方式一: 选择仓库的下拉菜单定义: 方式二:使用struts2的标签: 此时listKey表示option对应的value的属性值,listValue表示option中间的文本值;listKey和listValue都会对应javabean中的属性名称 7:(ajax)在in.jsp中,使用简记码和仓库id查询货物的时候 方式一:(普通方式) 不使用struts,此时可以使用 导入json需要的jar包 (1)在GoodsAction中添加: /**搜索货物 * @throws Exception */ public String searchGoods() throws Exception{ //设置request和response的ContentType为utf-8 ServletActionContext.getRequest().setCharacterEncoding("UTF-8"); ServletActionContext.getResponse().setContentType("text/html;charset=utf-8"); //使用简记码和仓库id查询惟一的货物信息,返回Goods Goods g = goodsService.findGoodsByNmAndStoreid(goods); if(g==null){ g = new Goods(); } //去掉g对象中的store和historys属性,使用JsonConfig操作,过滤不需要的属性 JsonConfig jsonConfig = new JsonConfig(); jsonConfig.setExcludes(new String[]{"store","historys"}); //将Goods对象使用json的数据格式返回(json数据是javascript能够解析的语言) JSONObject jsonObject = JSONObject.fromObject(g,jsonConfig); //向页面中响应数据 PrintWriter pw = ServletActionContext.getResponse().getWriter(); pw.print(jsonObject.toString()); pw.close(); return null; } (2)在in.jsp的页面上定义: 方案二:使用sturts2的json (1)导入struts2需要的插件包struts2-json-plugin-2.3.3.jar (2)在GoodsAction的方法中定义: public String searchGoods() throws Exception{ //使用简记码和仓库id查询惟一的货物信息,返回Goods Goods g = goodsService.findGoodsByNmAndStoreid(goods); if(g==null){ g = new Goods(); } //去掉g对象中的store和historys属性,使用JsonConfig操作,过滤不需要的属性 JsonConfig jsonConfig = new JsonConfig(); jsonConfig.setExcludes(new String[]{"store","historys"}); //将Goods对象使用json的数据格式返回(json数据是javascript能够解析的语言) JSONObject jsonObject = JSONObject.fromObject(g,jsonConfig); //将查询的结果放置到值栈中的message属性值中 goods.setMessage(jsonObject.toString()); return "searchGoods"; } (3)在struts.xml中定义: /jsps/goods/in.jsp message (4)在in.jsp中调用定义: 方案三:使用struts2的json,将对象的属性json化 (1)导入struts2需要的插件包struts2-json-plugin-2.3.3.jar (2)在GoodsAction中定义: public String searchGoods() throws Exception{ //使用简记码和仓库id查询惟一的货物信息,返回Goods Goods g = goodsService.findGoodsByNmAndStoreid(goods); if(g==null){ g = new Goods(); } //将g对象压入到栈顶,设置放置到栈顶的值的属性全部被json化 ServletActionContext.getContext().getValueStack().pop(); ServletActionContext.getContext().getValueStack().push(g); return "searchGoods"; } (3)在struts.xml文件中定义: /jsps/goods/in.jsp (4)在in.jsp中定义: 问题:此时返回页面的数据是栈顶对象的全部属性都被json化了,那么此时会影响加载的利率,毕竟指向加载某些属性,那怎么办呢? 解决方案:只需要修改struts.xml的配置: /jsps/goods/in.jsp name,unit,mode,amount 方案四:使用struts2的json,将json对象封装到某个对象中 (1)导入struts2需要的插件包struts2-json-plugin-2.3.3.jar (2)在GoodsAction中定义: public String searchGoods() throws Exception{ //使用简记码和仓库id查询惟一的货物信息,返回Goods goods = goodsService.findGoodsByNmAndStoreid(goods); return "searchGoods"; } (3)在struts.xml中定义: /jsps/goods/in.jsp action model.* (4)在in.jsp中 问题:此时返回页面的数据是model对象的封装的全部属性都被json化了,那么此时会影响加载的利率,此时指向加载某些属性,那怎么办呢? 解决方案:只需要修改struts.xml的配置: 只需要修改struts.xml的配置文件: /jsps/goods/in.jsp action model\.name,model\.unit,model\.mode,model\.amount 五:spring第五天 1:(ajax)在out.jsp,使用简记码nm获取对应的仓库集合List 方式一:使用传统方式JSONArray (1)在Action中定义: public String searchStore() throws Exception{ ServletActionContext.getRequest().setCharacterEncoding("UTF-8"); ServletActionContext.getResponse().setContentType("text/html;charset=utf-8"); //使用简记码,查询货物对应仓库的集合List List list = goodsService.findStoreListByNm(goods); //过滤掉不需要的属性goods JsonConfig jsonConfig = new JsonConfig(); jsonConfig.setExcludes(new String[]{"goods"}); JSONArray jsonArray = JSONArray.fromObject(list,jsonConfig); PrintWriter pw = ServletActionContext.getResponse().getWriter(); pw.print(jsonArray.toString()); pw.close(); return null; } (2)在页面中的js定义: