大概内容:
1.简介一下Spring的切面技术(AOP编程)
2.Spring的第一种切面技术的演示(经典的基于代理的AOP)
3.Spring的第二种切面技术的演示(通过Aspectj驱动的AOP)
Spring的切面技术(AOP编程)
Spring切面技术也叫AOP编程:通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态添加功能的技术;简单来说就是我们自己写一个与核心业务代码分离的单独的模块,在这个模块中我们可以实现特定的功能,在配置文件中配置核心业务需要使用到的功能,在编译时自动织入到业务模块中。
相关的术语:
通知(Advice):定义切面何时使用,以及要实现的功能
连接点(JoinPoint):程序应用通知的一个时机,这些”时机”就是连接点,例如方法被调用或方法抛出异常时等等
切点(PointCut):定义切面使用的地方(本质上可以说是一个捕捉连接点的结构);例如某个方法的名称
切面(Aspect):把实现特定功能的代码提取出来,封装成一个模块;切面=切点+通知
织入(Weaving):把切面应用到目标对象或者新创建的代理对象的过程
通知的类型:
前置通知(BeforeAdvice):在连接点之前被执行的通知
返回后通知(After returning advice):在连接点正常完成后执行的通知
抛出异常后通知(After throwing advice):在方法抛出异常后执行的通知
后置通知(After advice):在连接点之后执行的通知,且不论是正常返回还是异常退出都会执行
环绕通知(Around advice):在连接点前后都执行的通知
Spring目前有四种切面技术:
1.经典的基于代理的AOP
2.通过Aspectj驱动的AOP
3.基于@Aspectj注解的切面
4.基于Aspectj中的aop标签的切面
Spring的第一种切面技术的演示
经典的基于代理的AOP:下面以打篮球事件为例来演示一下吧!
1.直接先写一个带有打篮球事件的核心类:Person.java
/*
* 写一个实现具体事件的Person核心类
*/
public class Person {
public void run() {
System.out.println("有事没事打打篮球!");
}
}
2.在打篮球之前我们要先换好鞋并且要进行热身,打完球之后要休息一会再洗澡等等,这些基本事件如果都写到打篮球的方法中去就会显得冗余,所以我们可以写一个类来做这些基本的事,也就是AOP中的通知:MyAdvice.java
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
/**
* 这里分别实现了前置通知和返回后通知,要实现下面的两个接口
*/
public class MyAdvice implements MethodBeforeAdvice,AfterReturningAdvice{
/*
* 前置通知的方法
*/
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("打球之前先换鞋再热下身!");
}
/*
* 返回后通知的方法
*/
@Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
System.out.println("打完球之后休息一会再洗澡!");
}
}
3.接下来就是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: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
">
<!-- 配置我们自己写的实现打篮球事件的核心类 -->
<bean id="p" class="cn.hncu.demo1.Person"></bean>
<!-- 配置切点 -->
<bean id="cut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<!-- 这里通过正则表达式匹配所有的run方法 -->
<property name="pattern" value=".*run"></property>
</bean>
<!-- 把我们写的通知的类配置进来 -->
<bean id="advice" class="cn.hncu.demo1.MyAdvice"></bean>
<!-- 配置切面:切面=切点+通知 -->
<bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut" ref="cut"></property>
<property name="advice" ref="advice"></property>
</bean>
<!-- 我们还要配置一个代理工厂,生成代理对象 -->
<bean id="personProxyFactory" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="p"></property>
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>
</beans>
4.最后可以测试一下:
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestDemo {
@Test
public void t1(){
ApplicationContext context=new ClassPathXmlApplicationContext("cn/hncu/demo1/t1.xml");
//拿到代理后的对象
Person p=context.getBean("personProxyFactory",Person.class);
p.run();
}
}
控制台输出结果:
这个就是我们想得到的效果,但是配置文件我们还可以再修改的更简单一些,Spring提供给了我们自动代理的功能,可以让通知和切点自动匹配,修改后的配置文件如下:
<?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"
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
">
<!-- 我们需要配置Spring提供的自动代理的类 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
<!-- 切面=切点+通知 -->
<bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 这里直接配置正则表达式 -->
<property name="pattern" value=".*run"></property>
<!-- 这里配置通知 -->
<property name="advice" >
<!-- 把我们自己写的通知配置成内部Bean -->
<bean class="cn.hncu.demo1.MyAdvice"></bean>
</property>
</bean>
<!-- 这里还是要配置我们自己写的实现具体事件的核心类 -->
<bean id="p" class="cn.hncu.demo1.Person"></bean>
</beans>
再重新测试:
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestDemo {
@Test
public void t2(){
ApplicationContext context=new ClassPathXmlApplicationContext("cn/hncu/demo1/t2.xml");
//这里直接拿到的对象就是代理过的对象
Person p=context.getBean("p",Person.class);
p.run();
}
}
代码执行结果和上面是一样的,就不展示了!
Spring的第二种切面技术的演示
通过Aspectj驱动的AOP:相对于第一种切面技术,其实改动的地方就在于引入了” Aspectj “的概念,Aspectj有自己的包和切点语言;所以相应的只要改动一下配置文件:
<?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"
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
">
<!-- 配置Spring提供的自动代理的类 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
<!-- 这里配置的切面是基于Aspectj的类,切面=切点+通知 -->
<bean id="advisor" class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
<!-- 这里使用的是Aspectj自己的切点表达式 -->
<property name="expression" value="execution( * cn.hncu..*.*(..) )"></property>
<!-- 配置通知 -->
<property name="advice" >
<!-- 把我们自己写的通知配置成内部Bean -->
<bean class="cn.hncu.demo1.MyAdvice"></bean>
</property>
</bean>
<!-- 配置我们实现具体事件的核心类 -->
<bean id="p" class="cn.hncu.demo1.Person"></bean>
</beans>
这里测试一下结果:
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestDemo {
@Test
public void t3(){
ApplicationContext context=new ClassPathXmlApplicationContext("cn/hncu/demo1/t3.xml");
//这里直接拿到的对象就是代理过的对象
Person p=context.getBean("p",Person.class);
p.run();
}
}
/* 执行结果:
* 打球之前先换鞋再热下身!
* 有事没事打打篮球!
* 打完球之后休息一会再洗澡!
*/
总结:简单的介绍了一下Spring的切面技术,并演示了其中的两种:经典的基于代理的AOP、通过Aspectj驱动的AOP,但这都还只是切面技术最基础的应用,我们还要不断在Web项目中去实战演练才能慢慢掌握!