Spring 学习笔记
原先的 Spring 学习的时候的笔记 整理了一下 觉得还行 简单实用 共享一下
1 要点记录 2 3 spring Ioc 4 { 5 6 7 1 spring bean 在反射创建对象后 使用的是单例模式! 存在数据安全的问题 8 属性是被共享的! 9 如果该属性有数据,会引起线程安全的问题! 10 <bean id="helloWorid" class="cn.itcast.springTest.HelloWorid" scope="prototype" > </bean> 11 可以通过 XML 配置来设置 spring 默认是 单例的 scope="prototype" 就变成了 多例的了 12 13 2 <bean id="personService" class="com.myapp.core.beanscope.PersonService" scope="singleton" init-method="init" destroy-method="cleanUp"> 14 15 nit-method 是设置 spring 在创建bean节点里面的 对象 时 初始化的时候 调用的方法 16 注: 在构造 或 静态 后面调用 17 destroy-method 是设置 bean 节点创建的对象 在销毁的时候 调用的方法 18 注: 如果是多例 的时候 spring 不负责销毁动作 19 20 21 22 3 spring 容器 (反射出的类对象) 的 三种初始化方法 23 24 3.1 默认的是 调用 无参的构造函数 25 26 3.2 factory-method=“” 需要创建类中的 方法 通过该方法的到返回的类型 27 实例: 28 public class HelloWorldFactory { 29 public static HelloWorld getInstance(){ 30 return new HelloWorld(); 31 } 32 } 33 34 35 4 spring 中 只要声明一个 bean 节点 运行的时候就会创建一个对象 36 37 38 5 spring 在启动的时候 spring 容器配制中的类 就已经创建完成了! 39 40 6 控制容器中的类创建时候 41 lazy-init 这个属性中设置 42 <bean id="helloWorid" class="cn.itcast.springTest.HelloWorid" lazy-init="true" ></bean> 43 在启动spring容器的时候,spring容器配置文件中的类就已经创建完成对象了 44 lazy-init 45 default false 46 true 在context.getBean的时候才要创建对象 47 * 优点 48 如果该bean中有大数据存在,则什么时候context.getBean,什么时候创建对象 49 可以防止数据过早的停留在内存中,做到了懒加载 50 * 缺点 51 如果spring配置文件中,该bean的配置有错误,那么在tomcat容器启动的时候,发现不了 52 false 在启动spring容器的时候创建对象 53 * 优点 54 如果在启动tomcat时要启动spring容器, 55 那么如果spring容器会错误,这个时候tomcat容器不会正常启动 56 * 缺点 57 如果存在大量的数据,会过早的停留在内存中 58 <bean id="helloWorld" class="cn.itcast.spring0909.createobject.when.HelloWorl" lazy-init="true"></bean> 59 <bean id="person" class="cn.itcast.spring0909.createobject.when.Perso" lazy-init="true"></bean> 60 61 62 63 64 总结: 65 spring的IOC : 66 1 IOC :spring容器控制对象的生命周期 ; 前提条件 :在容器中的bean 必须是单例 67 1.1 : 创建 68 1.1.1 : 方式 69 利用默认的构造函数 70 利用静态工厂方法 71 利用实例工厂方法 72 1.1.2 : 时机 73 lazy-init 为"default/false" 当启动 spring 容器的时候 创建bean 74 但是如果bean 是 prototype(多例) 时,特殊。这种情况无效 75 76 ps:在spring启动的时候就会发现错误 ,有肯能照成一些数据长时间的驻留在内存中 77 78 lazy-init 为"true" 当context。getBean时创建 79 bean为多例时,必须用这种方式创建对象 80 81 PS: 不能及时发现错误, 数据会在需要的时候加载 82 83 1.2 : 初始化 84 1.2.1 : 由spring容器调用init 方法, 在构造函数之后执行 85 86 87 1.3 : 销毁 88 1.3.1 : 如果是单利,则必须返回CalssPathXmlApplicationContext该容器,才能执行销毁 89 1.3.2 : 如果是多例,则容器不负责销毁 90 91 } 92 93 spring DI 依赖注入 94 { 95 96 定义: 给属性赋值就是依赖注入 97 98 1.1 利用Set来赋值 99 { 100 <bean id="person" class="cn.spring.di.xml.set.Person"> 101 <!-- 102 property就是代表属性 103 在spring中基本类型(包装类型和String)都可以用value来赋值 104 引用类型用ref赋值 105 --> 106 <property name="pid" value="5"></property> 107 <property name="pname" value="王二"></property> 108 <property name="student"> 109 <ref bean="student"/> 110 </property> 111 <property name="lists"> 112 <list> 113 <value>list1</value> 114 <value>list2</value> 115 <ref bean="student"/> 116 </list> 117 </property> 118 <property name="sets"> 119 <set> 120 <value>set1</value> 121 <value>set2</value> 122 <ref bean="student"/> 123 </set> 124 </property> 125 <property name="map"> 126 <map> 127 <entry key="map1"> 128 <value>map1</value> 129 </entry> 130 <entry key="map2"> 131 <value>map2</value> 132 </entry> 133 <entry key="map3"> 134 <ref bean="student"/> 135 </entry> 136 </map> 137 </property> 138 <property name="properties"> 139 <props> 140 <prop key="prop1"> 141 prop1 142 </prop> 143 </props> 144 </property> 145 </bean> 146 } 147 148 1.2 利用构造函数赋值 149 { 150 <bean id="person" class="cn.spring.di.xml.constructor.Person"> 151 <!-- 152 构造函数的参数 153 index 第几个参数,下标从0开始 154 type 参数的类型 155 ref 如果类型是引用类型,赋值 156 value 如果类型是基本类型,赋值 157 说明: 158 只能指定一个构造函数 159 --> 160 <constructor-arg index="0" type="java.lang.String" value="露露"></constructor-arg> 161 <constructor-arg index="1" ref="student"></constructor-arg> 162 </bean> 163 164 <bean id="student" class="cn.spring.di.xml.constructor.Student"></bean> 165 166 类: 167 public Person(Long pid,String pname){ 168 this.pid = pid; 169 this.pname = pname; 170 } 171 172 public Person(String pname,Student student){ 173 this.pname = pname; 174 this.student = student; 175 } 176 177 } 178 179 180 } 181 182 183 IOC 和DI 做了什么事情? 184 185 * 创建对象 186 * 给对象赋值 187 188 IOC 和DI 的意思 189 190 * 可以在类中引用一个接口,而给接口赋值的工作交给spring容器来做,程序员只需要在配置文件中做一些配置就行。 191 这样在客户端做到了 完全的 面向接口编程 192 193 194 195 196 spring AOP : 197 198 { 199 * AOP 的核心就是动态代理; 200 其实就是 为目标类 方法进行一次包装 , 在方法执行前 和之后 触发其他模块方法执行 201 202 * springAOP的各个概念: 203 204 1. 切面: 205 切面指的就是 对目标类方法包装 时 提前或之后执行的方法的 类对象 206 2. 通知: 207 指得是 对目标类方法包装 时 提前或之后执行的方法 208 3. 切入点: 209 指的是 只有符合切入点,才能把通知和目标方法结合在一起 就是一个对目标方法的一个验证 210 4. 连接点: 211 指的是 客户端调用的方法 也就是具体调用的方法 212 213 * 代理对象的方法=通知+目标方法 214 * aop:做到了代码块的重用 215 216 217 关于SpringAOP的 配置 从spring_aop示例中找寻 218 219 关于切入点表达式 从 spring.xls 表格中找寻 220 221 222 223 springAOP 的原理: 224 225 226 227 * 加载配置文件,启动spring容器 228 * spring容器为bean创建对象 229 * 解析aop的配置,会解析切入点表达式 230 * 看纳入spring管理的那个类和切入点表达式匹配,如果匹配则会为该类创建代理对象 231 * 代理对象的方法体的形成就是目标方法+通知 232 * 客户端在context.getBean时,如果该bean有代理对象,则返回代理对象,如果没有代理对象则返回原来的对象 233 234 235 * 说明: 236 如果目标类实现了接口,则spring容器会采用jdk proxy, 如果目标类没有实现接口,则spring容器会采用 cglibproxy 237 238 239 240 241 PS: 有参数的通知方法: 242 243 通知有 前置 后置 异常 最终 环绕 这些类型 244 245 通长自己写的通知方法是用的是 无参方法, 如果需要获得 连接点 也就是客户端调用方法 的信息的时候 使用有惨方法 246 247 示例: 248 { 249 250 /** 251 * 前置通知 252 * 通过JoinPoint获取连接点的信息 253 */ 254 public void beginTransaction(JoinPoint joinPoint){ 255 joinPoint.getArgs();//获取方法的参数 256 String methodName = joinPoint.getSignature().getName(); 257 System.out.println(methodName); 258 System.out.println("begin transaction"); 259 } 260 /** 261 * 后置通知 ,Object val 是返回值 262 * 注意 在配置文件中 配置的后置通知XML 中 returning=”var“ 263 * 那么 定义方式的时候 返回值名称也得 和 配置文件中相同 264 * 265 * 如果目标方法遇到异常 ,改通知将不执行 266 * 267 */ 268 public void commit(JoinPoint joinPoint,Object val){ 269 List<Person> personList = (List<Person>)val; 270 System.out.println(personList.size()); 271 System.out.println("commit"); 272 } 273 /** 274 * 最终通知 // 在目标方法执行之后 275 * 无论目标方法是否遇到异常 ,都执行 经常做一些关闭资源的动作 276 */ 277 public void finallyMethod(){ 278 System.out.println("finally method"); 279 } 280 281 /** 282 * 异常通知 283 */ 284 public void exceptionMethod(Throwable ex){ 285 System.out.println(ex.getMessage()); 286 } 287 /** 288 * 环绕通知 289 * 能控制目标方法的执行 相当于拦截器方法 290 * @param joinPoint = 客户端调用的方法 291 * @throws Throwable 292 */ 293 public void aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable{ 294 System.out.println("aaaa"); 295 // 获取客户端调用方法的名称 296 String methodName = joinPoint.getSignature().getName(); 297 // 判断是否是 期望的方法 298 if("savePerson".equals(methodName)){ 299 // 是 就让 客户端调用的方法执行 300 joinPoint.proceed();// 301 } 302 // 否 客户端方法 不执行 303 } 304 305 306 } 307 308 PS: 前置通知 和 后置通知 只能在目标方法中 添加内容 ,但是控制不了方法的指向 309 310 重要提示: 通知是通过 配置文件的从上往下 执行的! 311 } 312 313 314 315 316 317 spring 中的 注解器 使用和运行机制 318 319 320 PS: @Resource 这个注解类 是最常用的 321 322 323 324 依赖注入的注解解析器 325 326 { 327 原理 328 329 * 启动spring容器,并且加载配置文件 330 * 会为student和person两个类创建对象 331 * 当解析到<context:annotation-config></context:annotation-config> 332 会启动依赖注入的注解解析器 333 * 会在纳入spring管理的bean的范围内查找看哪些bean的属性上有@Resource注解 334 * 如果@Resource注解的name属性的值为"",则会把注解所在的属性的名称和spring容器中bean的id进行匹配 335 如果匹配成功,则把id对应的对象赋值给该属性,如果匹配不成功,则按照类型进行匹配,如果再匹配不成功,则报错 336 * 如果@Resource注解的name属性的值不为"", 会把name属性的值和spring容器中bean的id做匹配,如果匹配 337 成功,则赋值,如果匹配不成功 ,则直接报错 338 * 说明: 339 * 注解只能用于引用类型 340 * @author Administrator 341 * 342 343 344 实例: 345 { 346 347 <!-- 348 1、导入命名空间 349 xmlns:context="http://www.springframework.org/schema/context" 350 http://www.springframework.org/schema/context 351 http://www.springframework.org/schema/context/spring-context-2.5.xsd 352 2、导入依赖注入的注解解析器 353 <context:annotation-config></context:annotation-config> 354 3、把student和person导入进来 355 --> 356 <context:annotation-config></context:annotation-config> 357 <bean id="student" class="cn.spring.di.annotation.Student"></bean> 358 <bean id="person" class="cn.spring.di.annotation.Person"></bean> 359 360 361 362 public class Person { 363 @Resource(name="student") 364 //@Autowired//按照类型进行匹配 365 //@Qualifier("student") 366 private Student studen; 367 368 } 369 public class Student { 370 public void say(){ 371 System.out.println("student"); 372 } 373 } 374 375 } 376 } 377 378 类扫描的注解解析器 379 { 380 381 原理 382 * 启动spring容器,加载配置文件 383 * spring容器解析到 384 <context:component-scan base-package="cn.spring.scan"></context:component-scan> 385 * * spring容器会在指定的包及子包中查找类上是否有@Component 386 * * 如果@Component注解没有写任何属性 387 * @Component 388 * public class Person{ 389 * 390 * } 391 * == 392 * <bean id="person" class="..Person"> 393 * 如果@Component("aa") 394 * @Component 395 * public class Person{ 396 * 397 * } 398 * == 399 * <bean id="aa" class="..Person"> 400 * * 在纳入spring管理的bean的范围内查找@Resource注解 401 * * 执行@Resource注解的过程 402 * 说明: 403 * xml效率比较高,但是书写比较麻烦 404 * 注解效率比较低,书写比较简单 405 * 不推荐实用 类扫描注解解析器 Spring Ioc 的目的就是解耦 如果控制 还在代码中写死 这种意义就不大 同时也不方便 阅读代码 406 * 407 408 实例 409 { 410 <!-- 411 1、导入命名空间 412 xmlns:context="http://www.springframework.org/schema/context" 413 http://www.springframework.org/schema/context 414 http://www.springframework.org/schema/context/spring-context-2.5.xsd 415 2、启动类扫描的注解解析器 416 3、启动依赖注入的注解解析器 417 --> 418 419 <!-- 420 component就是bean 421 base-package 422 会在base-package的值所在的包及子包下扫描所有的类 423 --> 424 <context:component-scan base-package="cn.spring.scan"></context:component-scan> 425 426 427 428 @Component("perso") 429 public class Person {} 430 @Component 431 public class Student {} 432 433 434 435 436 } 437 438 439 440 441 442 443 } 444 445 使用构造器 来控制 bean 加载的 初始化 和 销毁 方法, 446 447 实例: 448 { 449 450 @PostConstruct // 类构造器之后调用 451 public void init(){ 452 System.out.println("init"); 453 } 454 455 @PreDestroy // 在销毁动作之前调用 456 public void destroy(){ 457 System.out.println("destroy"); 458 } 459 460 } 461 462 463 PS: 如果一个类中有 基本类型 ,并且 基本类型是spring的形式来赋值。 这个时候必须使用XML来赋值,不能使用注解 赋值 464 465 PS: 不推荐使用 类型匹配 也就是 466 { 467 @Resource 468 private Student studenA; 469 这里找的是 bean 的 class 的值 470 471 如果定义多个bean class 的值 相同 那么就会报错 因为不确定是哪一个 bean 472 473 474 475 } 476 477 478 479 spring容器中的继承问题 480 481 1.1.1 默认情况下 即便给是 父类的属性注入了 对应的值 ,但是子类 并不会 的到父类的值 482 483 解决方法: 484 485 parent : 让子类拥有父类属性的值 486 487 <bean id="person" class="cn.springTest.Person" parent="personBase"></bean> 488 489 同样 可以让子类拥有父类的属性 自己赋值 不用写 parent 属性 490 <bean id="student" class="cn.spring.extend.Student"> 491 <property name="name" value="aaaaa"></property> 492 </bean> 493 494 495 496 abstract 497 spring容器不会为该类创建对象 498 499 <bean id="person" class="cn.spring.extend.Person" abstract="true"> 500 <property name="name" value="王二的哥"></property> 501 </bean> 502 503 504 505 Spring 事物问题: 506 507 508 { 509 * 详情请看 JDBC / transaction 文件下的示例 510 511 * 关键点 512 513 <!-- 声明事务通知 id事务标识 transaction-manager --> 514 <!-- 事务管理器 --> 515 <bean id="transactionManager" 516 class="org.springframework.jdbc.datasource. 517 DataSourceTransactionManager"> 518 <property name="dataSource"> 519 <ref bean="dataSource"/> 520 </property> 521 </bean> 522 523 Ps : id="transactionManager" 的Class 是 一个继承了一个抽象父类的 子类 目的是为了多态,由子类告诉Spring 使用数据库是那种技术 524 525 526 <!-- 声明目标方法中哪些方法需要事务,哪些不需要事务 --> 527 <tx:advice id="tx" transaction-manager="transactionManager"> 528 <tx:attributes> 529 <!-- name 限定方法的名称 isolation 隔离机制 propagation 传播机制 ready-only 只读 --> 530 <tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" 531 read-only="false" /> 532 </tx:attributes> 533 </tx:advice> 534 535 536 PS: propagation 传播机制 基本用的都是默认的 REQUIRED 指得是 一个方法中如果有多个事物方法 ,那么就把第一个事物方法后面的事物方法逐个附加到第一个事物方法中。 启动一次事物就行! 537 538 539 <!-- spring容器做的事情 --> 540 <aop:config> 541 <aop:pointcut 542 expression="execution(* cn.spring.jdbc.transaction. 543 PersonServiceImpl.*(..))" 544 id="perform" /> 545 546 Ps: 声明一个切面 的 触发点 547 548 <aop:advisor advice-ref="tx" pointcut-ref="perform"/> 549 550 Ps: 不需要自己声明切面类, 直接 创建切面 通知 指定到 事物方法 551 </aop:config> 552 Ps: 不经常写可能会 忘记怎么写 我的方式是 从下上往上写 先写 <aop:config> --> <tx:advice>--><bean> 553 } 554 555 556 557 558 559 Spring dataSource 定义数据库连接类的两种方式 560 561 * 562 563 <!-- 产生dataSource --> 564 <bean 565 class="org.springframework.beans.factory. 566 config.PropertyPlaceholderConfigurer"> 567 <property name="locations"> 568 <value>classpath:jdbc.properties</value> 569 </property> 570 </bean> 571 <bean id="dataSource" destroy-method="close" 572 class="org.apache.commons.dbcp.BasicDataSource"> 573 <property name="driverClassName" value="${jdbc.driverClassName}" /> 574 <property name="url" value="${jdbc.url}" /> 575 <property name="username" value="${jdbc.username}" /> 576 <property name="password" value="${jdbc.password}" /> 577 </bean> 578 579 PS: 读取 classpath:jdbc.properties 这个路径中 的 properties 配置文件中相同,将配置文件中对应的值赋 580 581 * properties 配置文件中的数据 582 jdbc.driverClassName=com.mysql.jdbc.Driver 583 jdbc.url=jdbc\:mysql\://localhost\:3306/hibernate 584 jdbc.username=root 585 jdbc.password=root 586 587 588 589 590 591 * 592 593 <!-- 配置dbcp的数据库连接池 --> 594 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> 595 <property name="driverClassName" value="com.mysql.jdbc.Driver" /> 596 <property name="url" value="jdbc:mysql://localhost:3306/hibernate" /> 597 <property name="username" value="root" /> 598 <property name="password" value="root" /> 599 </bean> 600 601 602 603 604 605 经验: 606 607 使用 AOP后 测试的时候 首先第一个先检查 获取的对象是否是个代理对象 608 609 610 配置Spring 文件的时候 思路 611 程序员 做什么 612 spring 做什么 613 614 615 616 关于 切面点 需要适用于 多个 包 的问题 617 618 expression="execution(* cn.spring.*.transaction.PersonServiceImpl.*(..))" 619 id="perform" /> 620 621 适用通配符 * 来模糊匹配 结节这个问题 622 623 624 625 626 627