本章主要包含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指的是方法的名字