本章主要包含Spring的AOP和Spring和Hibernate和Struts整合
后處理器
Bean后處理器會在Bean實例創建成功之后,對Bean實體進行進一步的增強處理。
Bean后處理器必須實現BeanPostProcessor接口,該接口中包含兩個方法postProcessAfterInitialization和postProcessBeforeInitialization,分別代表初始化之后執行和初始化之前執行,以下是一個小例子
package com.util;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("Bean后處理器的postProcessAfterInitialization方法");
System.out.println("beanName=" + beanName);
return bean;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("Bean后處理器的postProcessBeforeInitialization方法");
System.out.println("beanName=" + beanName);
return bean;
}
}
還可以通過bean標簽中init-method設置這個bean的初始化方法,用destory-method來設置這個bean的銷毀方法,這些方法是寫在這個bean所對應的類中的。
容器后處理器
除了創建Bean的后處理器之外還可以創建Spring容器的后處理器,我們只需要實現BeanFactoryPostProcessor接口即可,它里面有一個postProcessBeanFactory方法,方法參數ConfigurableListableBeanFactory就是Spring容器
屬性占位符配置器
想在xml文件中使用.propertiest文件中的內容就可以看看下面的代碼了。
在application.xml文件中加入
<bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
<property name="locations">
<list>
<value>classpath:dbconn.properties</value>
</list>
</property>
</bean>
<!-- 數據庫相關配置 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"
p:driverClass="${dataSource.driverClass}"
p:user="${dataSource.user}"
p:password="${dataSource.password}"
p:jdbcUrl="${dataSource.jdbcUrl}"
p:maxPoolSize="${dataSource.maxPoolSize}"
p:minPoolSize="${dataSource.minPoolSize}"
p:initialPoolSize="${dataSource.initialPoolSize}"
p:maxIdleTime="${dataSource.maxIdleTime}"
/>
之后在src文件夾下創建dbconn.properties,它的文件內容是
dataSource.driverClass = com.mysql.jdbc.Driver
dataSource.user = root
dataSource.password = root
dataSource.jdbcUrl = jdbc:mysql://localhost/spring
dataSource.maxPoolSize = 40
dataSource.minPoolSize = 2
dataSource.initialPoolSize=2
dataSource.maxIdleTime=30
Spring的“零配置”支持
如果不想用xml配置的話,也可以用annotation,在Spring中提供了一下幾個annotation來配置Spring Bean類
- @Component:標注一個普通的Spring Bean類
- @Controller:標注一個控制器組件類
- @Service:標注一個業務邏輯組件類
- @Repository:DAO組件類
配置好了這些annotation之后只需要在application.xml文件中加入
<context:component-scan base-package=""></context:component-scan>
來指定Spring自動搜索這些包下的Java類,并以注解來生成Bean實例。使用的時候要注意的是: - @Component可以帶一個字符串類型的參數用作bean的id
- @Scope可以帶“prototype”或者別的范圍來指定bean的使用范圍
- @Resourceo(name="beanName")可以用來配置依賴
- @PostConstruct和@PreDestroy來配置定制生命周期的行為,比如說init和close
- @Lazy可以帶一個boolean類型的參數,如果為true就不會預初始化該Bean
- @DependsOn({beanName1,beanName2})可以強制初始化bean1和bean2,就算bean1、bean2設置成@Lazy(true)
- @Autowired,是用來自動裝配的,可以在bean的set方法上加入@Autowired或者在構造函數上加入,哪怕參數有多個也不要緊,Spring就會自動按照類型搜索出合適的bean。@Autowired可以精確的利用類型(泛型)執行自動裝配,但是如果有兩個不同的bean他們的類型是同樣的可這么辦呢?這時我們可以通過@Qualifier來通過bean的id自動裝配
資源訪問
訪問類加載路徑里的資源
book.xml文件放在src文件下
ClassPathResource resource = new ClassPathResource("book.xml");
System.out.println(resource.getFilename());
System.out.println(resource.getDescription());
SAXReader reader = new SAXReader();
Document document = reader.read(resource.getFile());
Element root = document.getRootElement();
System.out.println("root="+root.getText());
List list = root.elements();
for(int i = 0; i < list.size(); i++) {
Element book = (Element) list.get(i);
System.out.println(i+":"+book.getText());
}
訪問文件系統里的資源
book.xml在與WebContent文件夾同一級
FileSystemResource resource = new FileSystemResource("book.xml");
訪問網絡資源
網絡資源需要使用InputStream流來讀取數據,不可以直接getFile的
UrlResource resource = new UrlResource("http://hxu0170466.my3w.com/book.xml");
System.out.println(resource.getFilename());
System.out.println(resource.getDescription());
SAXReader reader = new SAXReader();
Document document = reader.read(resource.getInputStream());
Element root = document.getRootElement();
System.out.println("root="+root.getText());
List list = root.elements();
for(int i = 0; i < list.size(); i++) {
Element book = (Element) list.get(i);
System.out.println(i+":"+book.getText());
}
與之類似的還有ServletContextResource訪問相對于ServletContext路徑下的資源、InputStreamResource訪問輸入流資源、ByteArrayResource訪問字節數組資源。
將Resource作為屬性
如果某一個Bean中需要使用某個文件的內容,那么我們就可以在這個Bean里面自動注入一個Resource實例,在配置bean的<bean>
標簽中加入 p:res="classpath:book.xml"
(這個bean有一個Resource類型的res屬性)這樣在這個bean中就可以獲取到類加載路徑下的book.xml文件的內容了。
在ApplicationContext使用資源
Resource r = applicationContext.getResource("book.xml")
就可以獲得到book.xml的文件內容了,這是以applicationContext的訪問策略來判定的,applicationContext是通過classpath來創建的那么book.xml就應該放在class加載路徑下。
Spring的AOP
AOP(Aspect Orient Programming),是指面向切面編程。與面向對象編程做對比的話可以這樣說,面向對象是從靜態的角度考慮程序結構,面向切面編程則是從動態角度考慮程序的運行過程。
為什么需要AOP
AOP可以在不修改源代碼的前提下增加系統的功能,如果只是用OOP的話如果要在某一個模塊上添加一個用戶合法驗證的話就需要在需要驗證的類里面添加驗證代碼,如果這種類有很多的話那么修改起來是很麻煩的,這時我們就可以通過使用AOP的方式添加一些新的功能(雖然我覺得攔截器也可以做到同樣的效果),常常用AOP來處理一些具有橫切性質的系統級服務,如事物管理、安全檢查、緩存和對象池管理等。總之,AOP要達到的效果是:保證在程序員不修改源代碼的前提下,位系統中業務組件的多個業務方法添加某種通用的功能。但AOP的本質是,依然要去修改業務組件的多個業務方法的源代碼——只是這個修改由AOP框架完成,而不是程序員。
AOP的基本概念
AOP框架的兩個特征
- 各步驟之間的良好隔離性
- 源代碼無關性
面向切面編程的一些術語
- 切面(Aspect):切面用于組織多個Advice,Advice放在切面中定義
- 連接點(Joinpoint):程序執行過程中明確的點,如方法的調用或者異常的拋出。<font color="red">Spring AOP中,連接點總是方法的調用</font>
- 增強處理(Advice):AOP框架在特定的切入點執行的增強處理。處理有“around”、“before”和“after”等類型
- 切入點(Pointcut):可以插入增強處理的連接點。簡而言之,當某個連接點滿足指定要求時,該連接點將被添加增強處理,該連接點也就變成了切入點。
- 引入:將方法或字段添加到被處理的類中。
- 目標對象:被AOP框架進行增強處理的對象。
- AOP代理:AOP框架創建的對象,簡單的說,代理就是目標對象的加強。
- 織入(Weaving):將增強處理添加到目標對象中,并創建一個被增強的對象(AOP代理)的過程就是織入。
Spring的AOP支持
Spring AOP采用的是基于代理的AOP方案,這與AspectJ采用的編譯時增強的解決方案不同。
AOP代理的方法 = 增強處理 + 目標對象的方法
基于注解的“零配置”方式(小示例)
beans.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
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-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<bean id="steelAxe" class="service.SteelAxe"/>
<bean id="british" class="service.British" p:axe-ref="steelAxe"/>
<context:component-scan base-package="service,com.aspect">
<context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
</context:component-scan>
<aop:aspectj-autoproxy/>
<aop:config proxy-target-class="true"/>
</beans>
Birtish.java
package service;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class British implements Person{
private Axe axe;
public void setAxe(Axe axe) {
System.out.println("Spring調用setAxe執行依賴注入");
this.axe = axe;
}
@Override
public String useAxe() {
// TODO Auto-generated method stub
System.out.println(axe.chop());
return "Slience";
}
}
Aspect類
package com.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class AuthAspect {
@Before("execution(* service.*.*(..))")
public void before() {
System.out.println("before");
}
@After("execution(* service.*.*(..))")
public void after() {
System.out.println("after");
}
@AfterReturning(returning="rvt", pointcut="execution(* service.*.*(..))")
public void afterReturning(Object rvt) {
System.out.println("afterReturning,返回值是:" + rvt);
}
@Around("execution(* service.*.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("around,執行之前");
Object rvt = joinPoint.proceed();
System.out.println("around,執行之后,返回值是:" + rvt);
//around還可以對返回值進行修改
rvt = "Hello World";
return rvt;
}
}
其中功能比較強大的就是@Around了,它既可以在目標方法之前織入也可以在目標方法之后織入而且可以決定目標方法在什么情況下執行,甚至可以完全阻斷目標方法的執行,但是如果要使用@Around最好在線程安全的情況下使用,平時還是使用@After和@Before,但是如果要改變返回值的話只有@Around可以實現了。
如果有多個Aspect可以使用@Order(value=1)來實現按照順序執行,value越大就越先執行。
如果需要獲取目標方法的調用參數可以這樣寫@AfterReturning("execution(* service.*.*(..)) && args(arg0, arg1)")
之后在下面的方法這樣就可以獲得那個方法的第一個和第二個參數了public void before(Object rvt, String arg0, String arg1)
如果覺得每次寫execution(* service.*.*(..)) && args(arg0, arg1)
太麻煩了可以在Aspect類里面寫上這個
@Pointcut("execution(* service.*.*(..))")
public void myPointcut(){};
之后調用這個execution可以這樣@Before("myPointcut()")
,如果別的Aspect類也想用這個Aspect類的myPointcut()所定義的execution的話,可以這樣寫@Before("AuthAspect.myPointcut()")
用類名.方法名的方式實現
基于XML配置文件的管理方式
如果將上面使用注解的例子改成使用XML配置的話就是這樣的
<bean id="authAspect" class="com.aspect.AuthAspect"/>
<aop:config proxy-target-class="true">
<aop:aspect id="authAspect" ref="authAspect">
<aop:before method="before" pointcut="execution(* service.*.*(..))"/>
<aop:after method="after" pointcut="execution(* service.*.*(..))"/>
</aop:aspect>
</aop:config>
其中的method指的是方法的名字