Spring
1. Spring简介 Spring是一个一站式轻量级框架,解决业务逻辑层和其他层的松耦合问题 优点:1.方便解耦,简化开发.通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring控制 2.AOP编程支持:面向切面编程 3.声明式事务的支持 4.方便程序测试 5.方便集成各种优秀框架 6.降低Java EE API的使用难度 7.Java源码是经典学习范例. 2. Spring IoC(Inversion of Control) 对象的控制反转 控制反转:将对象的创建权反转给(交给)Spring 业务和实现类太过紧密. 传统开发模式:通过工厂模式可实现,接口与实现业务的分离. 使用Spring-IoC:添加jar包,创建配置文件,添加配置文件,测试使用 3. Spring IoC的基本使用 创建一个新的project,在idea上面java E下面的Web Application 在web下面的web-inf下面新建一个lib的文件夹,添加相关jar包,然后对相关jar包add lib. 然后在src下面添加packeg com.itlike.demo1 添加interface UserDao 添加两个方法save和delete 两个class UserDaoMysql和UserDaoOracel,重写save和delete方法. 添加测试类 UserTest,写@Test test方法. 使用Spring IoC,而不是new一个对象 使用方式: 1.添加jar包 2.创建配置文件File,通常名称起为applicationContext.xml,放在src下面 3.添加配置选项.添加<bean id="" class=""/>添加的是id是自己想的一个名字可以随便写,class是接口实现类的地址全路径 applicationContext.xml的配置. <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns=XML namespace xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi=xml schema instance是指xml文件遵守xml规范 xsi:schemaLocation="http://www.springframework.org/schema/beans schemaLocation=模拟定位 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userDaoXml" class="com.itlike.demo1.UserDaoOracleImpl" init-method="myinit" destroy-method="destroy"> <!-- name:要注入属性的名称 value:注入(赋值)值 属性:要提供set方法 --> <property name="name" value="lk123456----99999"/> </bean> <!-- init-method:对象创建时,指定调用的方法 destroy-method:对象销毁时,指定调用的方法 scope: 指定对象的作用范围 单例 多例 request session --> <bean id="person" class="com.itlike.demo2.Person" init-method="init" destroy-method="destroy" scope="singleton" > <property name="name" value="itlike"/> </bean> <!--导入另外的配置文件--> <import resource="applicationContext2.xml"/> <import resource="applicationContext3.xml"/> </beans> 添加好了配置文件,需要在test中加载配置文件. (1).加载配置文件 先new一个ClassPathXmlApplicationContext("applicationContext.xml"); 然后ApplicationContext applicationContext = 这个new的对象. (2).根据id获取对象 UserDaoMysqlImpl userDaoXml = (UserDaoMysqlImpl) applicationContext.getBean("userDaoXml"); userDaoXml.save(); userDaoXml.delete(); //(需要对.getBean进行强制类型转换) 由于面向接口的原因,上面UserDaoMysqlImpl是UserDao的一个是类,所以使用UserDao的方法 把UserDaoMysqlImpl都换成UserDao.变成如下: UserDao userDaoXml = (UserDao) applicationContext.getBean("userDaoXml"); userDaoXml.save(); userDaoXml.delete(); 以后如果需要改变为Oracel只需要改变applicationContext.xml的bean的class就行了. idea通过右键generate getter and setter添加get和set方法. idea快捷键alt+insert添加各种方法.setter getter idea使用Ctrl+O,重写方法快捷键. IoC与DI:依赖注入前提必须有IOC的环境,Spring管理这个类的时候才能将类的依赖的属性值注入(设置)进来 依赖注入:给spring管理类当中依赖的属性,通过配置文件进行赋值的过程 4. Spring-DI依赖注入 依赖注入前提必须有IOC的环境 Spring管理这个类的时候才能将类的依赖的属性值注入(设置)进来 接上课,如果一个类已经交给Spring来管理了,那么这个类里面可能还有其他的属性. 比如里面还有一个String name.那么这个类就依赖于这个name的属性, 给spring管理类当中依赖的属性,通过配置文件进行赋值的过程就称为依赖注入 常规的赋值方式 @Test public void test2(){ //常规的方式进行赋值 UserDaoMysqlImpl userDaoMysql=new UserDaoMysqlImpl(); userDaoMysql.name="xxx"; System.out.println(userDaoMysql.name); } 使用依赖注入的方式. 该属性需要有setter方法,在application.xml里的bean的里面 添加一个<property name="xxx" value"">name为该属性的名字,value为你要赋的值. <bean id="userDaoXml" class="com.itlike.demo1.UserDaoMysqlImpl"> <!-- name:要注入属性的名称 value:注入(赋值)值 属性:要提供set方法--> <property name="name" value="赋值."></property> </bean> 创建一个test2 @Test public void test2(){ ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml"); UserDaoMysqlImpl B = (UserDaoMysqlImpl) applicationContext.getBean("userDaoXml"); System.out.println(B.name); } name的值通过.xml配置的方式进行赋值. 注意:1.实现类当中的属性要提供set方法 2.在配置文件当中配置依赖注入 5. Spring-工厂类 BeanFactory老版本使用方式,在以前当调用getBean的时候才会创建实例对象 现在使用的是ApplicationContext 新版本使用方式,当加载配置文件时候就会创建实例对象 ApplicationContext实现类 ClassPathXmlApplicationContext加载类路径下的配置文件//一般的使用方式. FileSystemXmlApplicationContext加载文件系统下的配置文件 6. Spring-Bean对象生命周期 Bean的相关配置. <bean id="person" class="com.itlike.demo2.Person" init-method="init" destroy-method="destroy" scope="singleton"> <property name="name" value="itlike"/> </bean> 名称与标识 id使用了约束中的唯一约束。里面不能出现特殊字符的 name没有使用约束中的唯一约束。里面可以出现特殊字符。 设置对象的生命周期方法 init-Method Bean被初始化的时候执行的方法 destroy-Method Bean被销毁的时候执行的方法(Bean是单例创建,工厂关闭) 使用application.close()实现关闭的时候调用destroy-Method方法. 单例工厂关闭的时候,里面所有的对象都会销毁. 7. Spring-bean对象的作用范围 作用范围的配置scope singleton 默认的,Spring会采用单例模式创建这个对象。 prototype 多例模式。 request 应用在web项目中,Spring创建这个类以后,将这个对象存入到request范围中。 session 应用在web项目中,Spring创建这个类以后,将这个对象存入到session范围中. globalsession 应用在web项目中,必须在porlet(基于Java的Web组件,子域名)环境下使用。但是如果没有这种环境,相对于session。 singleton 使用单例:关闭工厂 (所有的对象都 会销毁) 使用多例时,关闭工厂时不会销毁. 8. Spring-工厂实例化方式 无参构造-默认执行的 public Person(){ super(); System.out.println("执行了Person无参方法"); } 静态工厂实例化 在applicationContext.xml里的bean里 配置factory-method="creatPerson" 在person里添加必须是public static静态的 Person返回值必须为该类 createPerson()自己命名的方法 public static Person createPerson(){ System.out.println("createUser"); return new Person(); } 实例工厂实例化 在applicationContext.xml里配置 <bean id="person" class="com.itlike.demo2.Person" factory-method="createPerson"></bean> <bean id="person2" class="com.itlike.demo2.Person" factory-bean="person"></bean> 添加test ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext2.xml"); Person person=(Person)applicationContext.getbean("person2"); System.out.println(person); 9. Spring-分模块配置 在加载时,添加多个配置文件名称 ApplicationContext applicationContext= new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext2.xml"); 在一个配置文件当中引入另一个配置文件 在applicationContext.xml里添加导入其他的applicationContext. <!--导入另外的配置文件--> <import resource="applicationContext2.xml"/> <import resource="applicationContext3.xml"/> 10. Spring-构造方法式属性注入 构造方法的方式的属性注入 在class类里添加构造方法 public Student(String name, Integer age){ this.name = name; this.age = age; } 在配置文件的bean下面添加 <bean id="person2" class="com.itlike.demo2.Person"> <constructor-arg name="name" value="renhao"></constructor-arg> <constructor-arg name="number" value="1399000"></constructor-arg> </bean> Set方法的属性注入 通过在class类里添加属性的setter方法,在.xml配置文件内添加property name="属性名" value="赋值" 实现属性注入. 11. Spring-引用类型属性注入 Set方法设置对象类型的属性 如果student类型中的某个属性是非基本类型,而是依赖于其他类的一个属性. 添加 <bean id="dog" class="com.itlike.demo3.Dog"> <property name="name" value="wc"/> </bean> 在引用 <bean id="student" class="com.itlike.demo3.Student" scope="prototype"> <constructor-arg name="name" value="like"/> <constructor-arg name="number" value="999"/> <property name="dog" ref="dog.name" /> </bean> 注意:在两个类中都需要添加setter方法,在property里不是使用value赋值而是添加ref引用. 也可以重写dog类里面的toString方法,直接ref="dog"输出重写方法. 12. Spring-p名称空间属性注入 P名称空间的属性注入,使用p名称空间,就不需要写那那么多的property,减少属性注入. 使用时,在beans里要添加名称空间,xmlns:p="http://www.springframework.org/schema/p" 再在bean下面使用P名称空间进行配置就行 <bean id="dog2" class="com.itlike.demo3.Dog" p:age="10" p:name="wc" p:color="red" > </bean> test下面添加测试 Dog student2=(Dog)applicationContext.getBean("dog2") ; System.out.println(student2); 而且引用ref也可以使用 <bean id="student" class="com.itlike.demo3.Student" scope="prototype" p:dog-ref="dog2"></bean> p:dog-ref="dog2"表示Student里的dog属性引用dog2的bean配置 13. Spring-spEL表达式属性注入 <bean id="dog2" class="com.itlike.demo4.Dog"> <property name="name" value="#{'wc'}"/> <property name="age" value="#{10}"/> <property name="color" value="#{'red'}"/> </bean> 格式value="#{}" 字符串就使用''添加在{}里面, 如果是数字就直接添加在{}里面. 还可以使用spEL表达式注入添加代替引用 <property name="dog" value="#{dog2}"/> <property name="dogName" value="#{dog2.name}"/> 14. Spring-集合类型属性注入 数组类型集合
在class里添加数组attr和setter方法,重写toString 然后在applicationContext.xml里添加配置 <bean id="student" class="com.itlike.demo3.Student"> <property name="attr"> <list> <value>a</value> <value>b</value> <value>c</value> </list> </property> list集合的添加注入类似于数组 添加list属性和setter,添加配置同上. List: 它是一个有序的集合(元素存与取的顺序相同) 它可以存储重复的元素 Set: 它是一个无序的集合(元素存与取的顺序可能不同) 它不能存储重复的元素 set集合的添加注入方法 添加set属性和它的setter, 添加配置 <property name="mySet"> <set> <value>set a</value> <value>set b</value> <value>set c</value> </set> </property> Map集合 添加map属性和它的setter, 添加配置 <property name="myMap"> <map> <entry key="name1" value="map1"></entry> <entry key="name2" value="map2"></entry> <entry key="name3" value="map3"></entry> </map> </property> 15. Spring-IoC注解开发 在spring4之后,想要使用注解形式,必须得要引入aop的包-->Spring-aop-5.0.7.RELEASE.jar 在配置文件当中,还得要引入一个context约束 配置组件扫描,哪个包下的类型使用组合扫描<context:component-scan base-package="com" /> <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--指定注解扫描包--> <context:component-scan base-package="com.itlike.demo01"/> </beans> 在类开上添加注解@Component("user")相当于在.xml里配置的<bean id="xxx" class=""> @Component("user") public class User { public void say(){ System.out.println("hello"); } } 使用注解注入属性, 可以不用提供set方法,直接在直接名上添加@Value("值") @Value("myxq") private String name; 如果提供了set方法,在set方法上添加@Value("值"); private String name; @Value("myxq") public void setName(String name) { this.name = name; } 16. Spring-注解引用类型属性注入 @Component 修改一个类,将这个类交给Spring管理 相当于在配置文件当中配置<bean id="" class=""> 为了更好的进行分层,Spring可以使用其它三个注解,功能类似, 目前使用哪一个功能都一样,后期可能会添加一些属于各自的属性. @Controller对应web层 @Service对应service层 @Repository对应dao层 @Value 设置普通属性值 注入引用类型的3种方式. 第1种方式 @Autowired 设置对象类型的属性值 直接使用这种方式,是按照类型完全属性注入 不需要在注解上使用id名称 创建一个class类 @Component("dog") public class Dog { @Value("sang") public String name; } 在User里添加 @Autowired public Dog abc; test里添加 System.out.println(user.abc.name); 注意需要添加Dog类的注解@Component,表示这个类归Spring进行管理,但是可以不添加@Component的值. @Autowired会自动到Spring中寻找Dog这个类. @Autowired就是根据类型进行注入 第2种方式 习惯是按照名称完成属性注入,必须让@Autowired注解与@Qualifier一起使用 @Autowired @Qualifier("dog") //根据名称进行注入 public Dog dog; 第3种方式 单独使用@Resource @Resource(name = "dog") //这就是@Component("dog")相当于bean的id public Dog abc; 17. Spring-生命周期注解形式 @PostConstruct 初始化方法 相当于.xml里配置的init-method @PreDestroy 销毁方法 相当于.xml里bean配置的destroy-method @scope 作用范围 相当于.xml里的scope 作用范围,单例singleton 多例prototype 注意使用单例:关闭工厂(所有的对象都 会销毁) 使用多例时,关闭工厂时不会销毁. 18 Spring-xml配置与注解结合 XML与注解比较,XML可以适用任何场景,结构清晰,维护方便, 注解不是自己提供的类使用不了,开发简单方便,注入简单 XML与注解整合开发,XML管理Bean,注解完成属性注入,使用过程中,可以不用扫描,扫描是为了类上的注解 不添加<context:component-scan base-package="com.itlike.demo01" />的情况下 在没有扫描的情况下添加.xml里<context:annotation-config/>, 使用属性注解@Resource @Value @Autowired @Qulifier
19 Spring-服务层与dao层传统写法 20. Spring-服务层与dao层spring配置写法 impl=implement 实施,执行; 1.配置.xml配置2个bean然后service引用dao方法 <bean id="dao" class="com.itlike.dao.UserDaoImpl"> </bean> <bean id="service" class="com.itlike.service.UserServiceImpl"> <property name="userDao" ref="dao"></property> </bean> 2.在UserServiceImpl里添加userDao和setter方法. public class UserServiceImpl implements UserService{ private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public void delete() { userDao.delete(); } } 3.在UserTest中导入配置,然后使用service.delete() @Test public void test() { ApplicationContext applicationContext= new ClassPathXmlApplicationContext("applicationContext.xml"); UserServiceImpl service=(UserServiceImpl)applicationContext.getBean("service"); service.delete(); } 21. Spring-服务层与dao层spring注解式写法 在.xml里添加一个扫描 <context:component-scan base-package="com.itlike"/> 在服务层添加@Service("service") 在Dao层添加@Repository("dao") 在服务层添加 @Autowired private UserDao userDao; 当Dao层只有一个bean时,直接使用@Autowired,如果有多个bean时, 需要使用@Qualifier("dao")指定使用的bean.或者使用@Resource(name="dao2") 22. Spring-AOP简介 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。 AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。 AOP采用了横向抽取机制取代了传统纵向继承. 不破坏原理的类,生成一个代理类,在原来类的基础上进行增强,可以随时添加,取消添加操作. 核心就是使用动态代理. 23. Spring-AOP内部原理JDK动态代理 JDK动态代理必须依赖于接口. 当使用接口的时候,自动调用JDK动态代理,而 添加一个GoodsJDKProxy代理类,使用代理类来处理. public class GoodsJDKProxy { public GoodsDao createProxy(GoodsDao goodsDao){ //增强 GoodsDao goodsDaoProxy =(GoodsDao) Proxy.newProxyInstance(goodsDao.getClass().getClassLoader(), goodsDao.getClass().getInterfaces(), new InvocationHandler() { @Override/*当调用对象的时候 , 所有的方法都会来到这里*/ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("save".equals(method.getName())) { System.out.println("调用权限校验"); return method.invoke(goodsDao,args); } return method.invoke(goodsDao,args); } }); return goodsDaoProxy; } } GoodsDaoTest代码 @Test public void test() { GoodsDao goodsDao = new GoodsDaoImpl(); // goodsDao.save(); GoodsJDKProxy goodsJDKProxy=new GoodsJDKProxy(); //获取的代理对象 GoodsDao proxy=goodsJDKProxy.createProxy(goodsDao); proxy.save(); proxy.update(); }
24. Spring-AOP内部原理Cglib动态代理 Cglig是一个第三方开源代码 生成类库,动态添加类的属性和方法。 在spring的核心包当中已经引入了cglib,采用的是继承方式来产生的代理对象. 没有接口时自动选择cglib 提供一个class,而不是接口 public class UserDao { public void save(){ System.out.println("保存"); } public void update(){ System.out.println("更新"); } } Test文件 public class UserDaoTest { @Test public void test(){ UserDao userDao=new UserDao(); //增强 UserDaoCglibProxy userDaoCglibProxy=new UserDaoCglibProxy(); UserDao cglibProxy=userDaoCglibProxy.createCglibProxy(userDao); cglibProxy.save(); cglibProxy.update(); } } 添加一个UserDaoCglibProxy代理类 public class UserDaoCglibProxy { public UserDao createCglibProxy(UserDao userDao){ //1.创建一个核心类 Enhancer enhancer=new Enhancer(); //2.设置父类 使用继承方式 创建一个子类自动继承UserDao enhancer.setSuperclass(userDao.getClass()); //3.设置回调 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { if ("save".equals(method.getName())) { System.out.println("权限认证"); return methodProxy.invokeSuper(o,objects); } return methodProxy.invokeSuper(o,objects); } }); //4.创建代理对象 就是把子类给你 UserDao obj =(UserDao) enhancer.create(); return obj; } } AOP思想最早是由AOP联盟组织提出的,Spring使用这种思想最好的框架 AspectJ,Spring的AOP有自己的实现方式,但是这种方式非常繁琐 AspectJ 是一个AOP的框架,Spring放弃了自己传统的方式 ,引入AspectJ作为自身AOP的开发 25. Spring-AOP相关术语 (1).Joinpoint:连接点,可以被拦截到的方法,能够被增强的方法,这些方法就可以称为是连接点 public class UserDao { public void save(){} public void update(){} } 这个save()和update()方法就称为连接点. (2).Pointcut:切入点,真正被拦截的方法,真正被增加的方法. 如上面我们在代理对象里给save()方法添加了一个权限检查功能, 这个save()方法就称为切入点. (3).Advice:通知.增加的内容,通常都是封装成一个方法, 这个方法我们就称为通知 比如上面给save()添加的权限检查功能 (4).Introduction:引介 类层面的增加 给原有的类添加一些新的属性方法 在开发中通常都是给方法进行增加 比如给上面的UserDao内部添加的一个public void delete()方法.这个delete方法就称为引介 (5).Target:被增加的对象.需要增强的对象,这个UserDao就称为Target (6).Weaving:织入 将通知应用到目标对象的过程,给切入点添加通知的过程 (7).Proxy:代理对象 (8).Aspect:切面 多个通知和多个切入点的集合 26. Spring-spring-test使用 1.引入spring基本jar包 2.引入aop开发的相关jar包 3.配置文件中引入aop约束 配置applicationContext.xml的beans. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> </beans> 4.Spring测试时,每次都需要获取工厂 通过spring-test,就不用每次获取添加测试依赖包spring-test-5.0.7.RELEASE.jar. 5.测试 @RunWith(SpringJUnit4ClassRunner.class) //让测试运行于spring测试环境 @ContextConfiguration("classpath:applicationContext.xml") //指定 Spring 配置文件所在的位置 public class AopTest { @Autowired @Qualifier("goodsDao") //或者使用@Resource(name="goodsDao")用于注入其他非基本类的属性 private GoodsDao goodsDao; @Test public void test(){ goodsDao.save(); goodsDao.update(); } }} } 27 Spring-AOP配置 上一章配置好测试环境. 6.编写一个切面类 public class Myaspect { public void checkPrivilege(){ System.out.println("权限认证"); } } 7.将切面交给spring <bean id="goodsDao" class="com.itlike.demo01.GoodsDaoImpl"></bean> <bean id="myaspect" class="com.itlike.demo02.Myaspect"></bean> 8.配置AOP完成对目标产生代理 <aop:config> <!--切点:给哪个方法增强--> <aop:pointcut id="pointcut" expression="execution(* com.itlike.demo01.GoodsDaoImpl.save(..))"/> //*代表任意方式都可以 <!--配置切面:增强是功能是什么--> //..代表任意参数都行 <aop:aspect ref="myaspect"> <!--前置通知--> <aop:before method="checkPrivilege" pointcut-ref="pointcut"/> </aop:aspect> </aop:config>
28. Spring-AOP通知类型 前置通知 aop:before:在目标方法执行之前,进行操作 <aop:pointcut id="pointcut" expression="execution(* com.itlike.demo01.GoodsDaoImpl.save(..))"/> <aop:before method="checkPrivilege" pointcut-ref="pointcut"/> 后置通知 aop:after-returning 在目标方法执行之后 进行操作 <aop:pointcut id="updatecut" expression="execution(* com.itlike.demo01.GoodsDaoImpl.update(..))"/> <aop:after-returning method="log" pointcut-ref="updatecut" returning="res"/> 通知 public void log(Object res){ System.out.println("日志记录"+res); } 环绕通知 aop:around 在目标方法执行之前 和之后进行操作.还可以阻止原方法的执行 <aop:pointcut id="delete" expression="execution(* com.itlike.demo01.GoodsDaoImpl.delete(..))"/> <aop:around method="around" pointcut-ref="delete"/> 通知 public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("开启事务"); Object proceed = joinPoint.proceed(); //此处继续执行delete操作,如果没有这句将不执行delete操作 System.out.println("提交事务"); return proceed; } 异常抛出通知 aop:after-throwing 在程序出现异常时进行操作,除了异常时执行,还可以获取信息 <aop:pointcut id="find" expression="execution(* com.itlike.demo01.GoodsDaoImpl.find())"/> <aop:after-throwing method="exceptionM" pointcut-ref="find" throwing="ex"/> 通知 private void exceptionM(Throwable ex){ System.out.println("异常终止"+ex.getMessage()); } 最终通知 aop:after 无论代码是否有异常,都会执行 <aop:after method="endding" pointcut-ref="find"/> 通知 public void endding(){ System.out.println("终止操作"); } 29. Spring-AOP切入点表达式 基于execution函数完成 语法【访问修饰符】 方法返回值 包名.类名.方法名(参数) public com.myxq.demo2.GoodsDaoImpl.save(..) 参数为任意参数 * com.myxq.demo2.GoodsDaoImpl.save(..) * 任意类型 * com.myxq.demo2.GoodsDaoImpl+.save(..) + 当前类和子类 * com.myxq..*.*(..) com.myxq包以及子包下面所有类的所有方法 30. Spring-AOP注解形式 1.引入Jar包 2.引入配置文件 3.编写切面类配置.xml里添加管理 创建切面 public class GoodsDaoAspect { public void log(){ System.out.println("日志"); } } <bean id="goodsAspect" class="com.itlike.deom1.GoodsDaoAspect"/> 4.使用注解的AOP对象目标类进行增强 在applicationContext.xml里配置aop注解自动开启 <!--aop开启注解--> <aop:aspectj-autoproxy/> 然后在切面类上添加注解 @Aspect public class GoodsDaoAspect { @Before(value = "execution(* com.itlike.deom1.GoodsDaoImpl.save())") public void log(){ System.out.println("日志"); } } 31. Spring-AOP注解形式通知类型 注解AOP通知类型 @Before就是前置通知 @AfterReturning后置通知 没有返回值 @AfterReturning(value = "execution(* com.itlike.deom1.GoodsDaoImpl.update())") public void houzhi(){ System.out.println("后置"); } 有返回值的情况 @AfterReturning(value = "execution(* com.itlike.deom1.GoodsDaoImpl.update(..))",returning ="Res") public void houzhi(Object Res){ System.out.println("后置"+Res); } @Around环绕通知 @Around(value = "execution(* com.itlike.deom1.GoodsDaoImpl.delete(..))") public Object Around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("环绕前."); Object obj = joinPoint.proceed(); System.out.println("环绕后."); return obj; } @AfterThrowing异常抛出通知 没有获取异常信息 获取异常信息 @AfterThrowing(value = "execution(* com.itlike.deom1.GoodsDaoImpl.find(..))",throwing = "ex") public void afterThrowing(Throwable ex){ System.out.println("异常"+ex); } @After最终通知 @After(value="execution(* com.itlike.deom1.GoodsDaoImpl.find(..))") public void after(){ System.out.println("最终通知"); } 32. Spring-AOP内部代理机制 @Before(value = "execution(* com.itlike.deom1.GoodsDaoImpl.save(..)) || execution(* com.itlike.deom1.GoodsDaoImpl.update(..))") public void log(){ System.out.println("日志"); } AOP的注解切入点的配置使用,有时候一个方法要添加前置通知对又要添加异常通知,又要添加最终通知 可以在切面当中定义好切入点,在通知当中直接使用定义好的切入点表达式。 @Pointcut(value = "execution(* com.itlike.demo1.GoodsDaoImpl.save(..))") private void pointcut1(){} @Pointcut(value = "execution(* com.itlike.demo1.GoodsDaoImpl.update(..))") private void pointcut2(){} @Pointcut(value = "execution(* com.itlike.demo1.GoodsDaoImpl.delete(..))") private void pointcut3(){} @Pointcut(value = "execution(* com.itlike.demo1.GoodsDaoImpl.find(..))") private void pointcut4(){} @Pointcut(value = "execution(* com.itlike.demo1.UserDao.save(..))") private void pointcut5(){} 定义多个切入点,一个通知同时定义到多个方法当中 @Before(value = "GoodsDaoAspect.pointcut1() || GoodsDaoAspect.pointcut4() || GoodsDaoAspect.pointcut5()") public void log(){ System.out.println("日志"); } @AfterReturning(value = "GoodsDaoAspect.pointcut2()",returning = "res") public void afterreturning(Object res){ System.out.println("后置通知---"+res); } 当使用接口时与不使用接口内部代理区别,使用接口。 使用接口时采用JDK动态代理,不是接口时使用Cglib.
33. Spring-JDBC模板使用 Spring的JDBC的模板,Spring是EE开发的一站式的框架,有EE开发的每层的解决方案。 Spring对持久层也提供了解决方案:ORM模块和JDBC的模板。 Spring提供了很多的模板用于简化开发, JDBC(org.springframework.jdbc.core.jdbc.jdbcTemplate) 和Hibernate(orm.springframework.orm.hibernamte.HibernateTemplate) JDBC模板使用的入门,引入jar包 spring开发基本jar包,数据库驱动,Spring的JDBC模板的jar包 创建数据库和表 create table account( id int primary key auto_increment, name varchar(20), money double ); 使用JDBC的模板 public class SpringJdbcTest { @Test public void test() { //创建连接池 DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql:///spring"); dataSource.setUsername("root"); dataSource.setPassword("cqkill000"); //创建jdbc模板 JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource); jdbcTemplate.update("insert into account values (null ,?,?)","itlike",1000d); } } 34. Spring-JDBC模板改写 将连接池和模板交给Spring管理,配置文件配置Bean 配置的beans <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> </beans> 配置Bean <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///spring"/> <property name="username" value="root"/> <property name="password" value="cqkill000"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="datasource"/> //使用ref,因为非基本类型的注入 </bean> 使用jdbcTemplate注解插入数据 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringJdbcTest2 { // @Autowired // @Qualifier("jdbcTemplate") @Resource(name = "jdbcTemplate") private JdbcTemplate jdbcTemplate; @Test public void test(){ jdbcTemplate.update("insert into account values (?,?,?)",2,"chen",101); } } 35. Spring-JDBC第三方连接池配置 DBCP 引入jar包 com.springsource.org.apache.commons.dbcp-1.2.2.osgi.jar com.springsource.org.apache.commons.pool-1.5.3.jar 配置DBCP连接池 <!--DBCP连接池--> <bean id="dbcp" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///spring"/> <property name="username" value="root"/> <property name="password" value="cqkill000"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dbcp"/> </bean> C3P0配置 <!--C3P0连接池--> <bean id="c3p0" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql:///spring"/> <property name="user" value="root"/> <property name="password" value="cqkill000"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="c3p0"/> </bean> DRUID配置 //阿里巴巴的开源连接池,目前性能最好? <!--DRUID连接池--> <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///spring"/> <property name="username" value="root"/> <property name="password" value="cqkill000"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="druid"/> </bean> 36. Spring-加载properties属性文件 创建一个file文件jdbc.properties driverClass=com.mysql.jdbc.Driver url=jdbc:mysql:///spring username=root password=cqkill000 加载属性文件,使用<bean>方式. <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="jdbc.properties"/> </bean> <!--DRUID连接池--> <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${driverClass}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </bean> 加载属性文件,使用<context/>方式 修改jdbc.properties里的配置,由于<!--key值 不能和name 一样--> jdbc.driverClass=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql:///spring jdbc.username=root jdbc.password=cqkill000 使用<context/>方式 value值 不能和name一样 <context:property-placeholder location="classpath:jdbc.properties"/> <!--DRUID连接池--> <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driverClass}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>
37. Spring-JDBC模板CRUD操作 插入操作jdbcTemplate.update("insert into 表名 value(?,?,?)",1,"xxx",123); //?占位符 删除操作jdbcTemplate.update("delete from 表名 where id=?",13); 更新操作jdbcTemplate.update("update 表名 set name=?,money=? where id=?","xxx",120,2); //where前面没有逗号. 查询操作 查询某一个字段 public void test4(){ //String.class 查询结果的类型 String name=jdbcTemplate.queryForObject("select name from account where id =?",String.class,3); System.out.println(name); } 查询count @Test public void queryCount(){ Long count=jdbcTemplate.queryForObject("select count(*) from account",Long.class); System.out.println(count); } 38. Spring-JDBC模板查询所有 查询操作 查询一个对象,返回对象的集合 先建立一个简单的pojo类Account,添加它的set和get方法,已经重写toString方法. public class Account { private Integer id; private String name; private Double money; } @Test public void queryAccount(){ Account account=jdbcTemplate.queryForObject("select * from account where id=?",new MyRowMap(),2); System.out.println(account); } class MyRowMap implements RowMapper<Account>{ @Override public Account mapRow(ResultSet resultSet, int i) throws SQLException { Account account=new Account(); account.setId(resultSet.getInt("id")); account.setName(resultSet.getString("name")); account.setMoney(resultSet.getDouble("money")); return account; } } 查询多个对象,返回对象的集合 @Test public void queryAllAccount(){ List<Account> query=jdbcTemplate.query("select *from account",new MyRowMap()); for (Account a:query){ System.out.println(a); } } 39. Spring-事务概述 什么是事务:逻辑上的一组操作,组成这组操作的各个单元,要么全都成功,要么全都失败。 事务的特性:ACID性质. 1.原子性 Atomicity:事务不可分割 2.一致性 Consistency:事务执行前后数据完整性保持一致 3.隔离性 Isolation:一个事务的执行不应该受到其他事务的干扰. 4.持久性 Durability:一旦事务结束,数据就持久化到数据库. 如果不考虑隔离性引发安全性问题 读问题: 1.脏读 :一个事务读到另一个事务未提交的数据 2.不可重复读 :一个事务读到另一个事务已经提交的update的数据,导致一个事务中多次查询结果不一致 3.虚读、幻读 :一个事务读到另一个事务已经提交的insert的数据,导致一个事务中多次查询结果不一致。 写问题: 丢失更新. 解决读问题,设置事务的隔离级别 1.可读取未提交 Read uncommitted :未提交读,任何读问题解决不了。 2.可读已提交 Read committed :已提交读,解决脏读,但是不可重复读和虚读有可能发生。 Orical默认级别 3.可重复读 Repeatable read :重复读,解决脏读和不可重复读,但是虚读有可能发生。 MySQL默认级别 4.序列化 Serializable :解决所有读问题。 性能损耗大 Spring的事务管理的API 1.PlatformTransactionManager PlatformTransactionManage:平台事务管理器 是一个接口,下面有两个实现类 1.DataSourceTransactionManager,底层使用JDBC管理事务. 2.HibernateTransactionManager,底层使用Hibernate管理事务. TransactionDefinition:事务定义信息:用于定义事务的相关的信息,隔离级别、超时信息、传播行为、是否只读 TransactionStatus:事务状态:用于记录在事务管理过程中,事务的状态的对象。 事务管理的API的关系:Spring进行事务管理的时候,首先平台事务管理器根据事务定义信息进行事务的管理, 在事务管理过程中,产生各种状态,将这些状态的信息记录到事务状态的对象中。 40. Spring-事务传播行为 什么是传播行为,一个业务方法当中,调用另一个业务的方法. Spring中提供了七种事务的传播行为: 保证多个操作在同一个事务中: 1.PROPAGATION_REQUIRED 默认值,如果A中有事务,使用A中的事务,如果A没有,创建一个新的事务,将操作包含进来 2.PROPAGATION_SUPPORTS 支持事务,如果A中有事务,使用A中的事务。如果A没有事务,不使用事务。 3.PROPAGATION_MANDATORY 如果A中有事务,使用A中的事务。如果A没有事务,抛出异常。 保证多个操作不在同一个事务中: 4.PROPAGATION_REQUIRES_NEW 如果A中有事务,将A的事务挂起(暂停),创建新事务,只包含自身操作。 如果A中没有事务,创建一个新事务,包含自身操作。 5.PROPAGATION_NOT_SUPPORTED 如果A中有事务,将A的事务挂起。不使用事务管理。 6.PROPAGATION_NEVER 如果A中有事务,报异常。 嵌套式事务: 7.PROPAGATION_NESTED 嵌套事务,如果A中有事务,按照A的事务执行,执行完成后,设置一个保存点 执行B中的操作,如果没有异常,执行通过,如果有异常,可以选择回滚到最初始位置,也可以回滚到保存点 41. Spring-事务问题 搭建Spring事务管理环境: 1.创建AoccuntDao 2.实现Dao接口 3.把Dao交给Spring管理 5.在Dao中注入数据源,在DAO当中注入jdbc模板,要保证dao继承了JdbcDaoSupport public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao{ // private JdbcTemplate jdbcTemplate; // // public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { // this.jdbcTemplate = jdbcTemplate; // } 继承之后, 就有了datasource的set方法,就可以注入了 通过两步注入 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="druid"/> </bean> <!--accountDao--> <bean id="accountDao" class="com.itlike.demo3.AccountDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"/> </bean> Dao继承JdbcDaoSupport,DAO注入JDBC模板 直接使用继承的JdbcDaoSupport里面的dataSource <!--accountDao--> <bean id="accountDao" class="com.itlike.demo3.AccountDaoImpl"> <property name="dataSource" ref="druid"/> </bean> 6.创建Account业务 public class AccountServiceImpl implements AccountService{ private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void transferMoney(String from, String to, Double money) { this.accountDao.addMoney(to,money); this.accountDao.minusMoney(from,money); } } 7.配置service 交给spring 并注入dao <!--accountDao--> <bean id="accountDao" class="com.itlike.demo3.AccountDaoImpl"> <property name="dataSource" ref="druid"/> </bean> <!--accountService--> <bean id="accountService" class="com.itlike.demo3.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> </bean> 8.测试 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class AccountTest { @Resource(name="accountService") private AccountService accountService; @Test public void Test(){ accountService.transferMoney("c3p0" ,"chen",100d); } }
42. Spring-编程式事务 需要手动编写代码 配置平台事务管理器 <!--配置事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> Spring提供了事务管理的模板类 <!--配置事务管理模板--> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"/> </bean> 在业务层注入事务管理的模板 <!--accountService--> <bean id="accountService" class="com.itlike.demo3.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> <property name="transactionTemplate" ref="transactionTemplate"/> </bean> 编写事务管理的代码 private TransactionTemplate transactionTemplate; public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } @Override public void transferMoney(String from, String to, Double money) { this.transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { accountDao.addMoney(to,money); int i=1/0; accountDao.minusMoney(from,money); } }); 43. Spring-声明式事务XML方式 引入aop的开发包 在beans中添加 xmlns:tx="http://www.springframework.org/schema/tx" xsi: http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd 配置事务管理器 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> AOP的配置 <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!--把增加织入到对应的方法里--> <aop:config> <aop:pointcut id="poincut" expression="execution(* com.itlike.demo4.AccountServiceImpl.transferMoney(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="poincut"/> </aop:config> 44. Spring-声明式事务注解方式添加 配置事务管理器 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> 开启注解事务 <!--开启注解 增强--> <tx:annotation-driven transaction-manager="transactionManager"/> 在业务层添加注解 @Transactional //在此可以添加参数就是7种事务传播的方式 public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void transferMoney(String from, String to, Double money) { accountDao.addMoney(to,money); accountDao.minusMoney(from,money); } } 45. Spring-整合web项目解决方案 在Servlet当中直接加载配置文件,获取对象 存在问题 每次请求都会创建一个Spring的工厂,这样浪费服务器资源,应该一个项目只有一个Spring的工厂。 在服务器启动的时候,创建一个Spring的工厂。 创建完工厂,将这个工厂类保存到ServletContext中。 每次使用的时候都从ServletContext中获取。 解决方案 使用spring核心监听器ContextLoaderListener 1.引入jar包 spring-web.jar 2.配置监听器 <!-- Spring的核心监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 加载Spring的配置文件的路径的,默认加载的/WEB-INF/applicationContext.xml --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> 3.直接在Action当中获取工厂 46. Spring-整合web项目代码实现 在wem.xml下面添加监听器的配置 <!-- Spring的核心监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 加载Spring的配置文件的路径的,默认加载的/WEB-INF/applicationContext.xml --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> ContextLoaderListener的作用就是启动Web容器时,自动装配ApplicationContext.xml的配置信息。 在servlet里写 ServletContext sc = this.getServletContext(); /*获取工厂 程序启动的时候 保存到serServletContext*/ WebApplicationContext applicationContext= WebApplicationContextUtils.getWebApplicationContext(sc); /*获取对象*/ UserService accountService=(UserService)applicationContext.getBean("accountService"); accountService.save(); 通过监听的形式去获取配置文件