目录
三、Spring的AOP的开发(AspectJ的XML的方式)
一、什么是AOP
1.AOP概念
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
2.应用场景
日志、性能监控等
二、Spring底层的AOP实现原理
动态代理
-
JDK动态代理 :只能对实现了接口的类产生代理
-
Cglib动态代理 (类似于Javassist第三方代理技术) :对没有实现接口的类产生代理对象,生成子类对象。
1.JDK动态代理
使用jdl动态代理来增强方法:
创建一个UserDao接口:
package com.rosinante.spring.demo1;
public interface UserDao {
public void save();
public void update();
public void find();
public void delete();
}
以及它的实现类:
package com.rosinante.spring.demo1;
public class UserDaoImpl implements UserDao{
public void save() {
// TODO Auto-generated method stub
System.out.println("保存用户...");
}
public void update() {
// TODO Auto-generated method stub
System.out.println("修改用户...");
}
public void find() {
// TODO Auto-generated method stub
System.out.println("查询用户...");
}
public void delete() {
// TODO Auto-generated method stub
System.out.println("删除用户...");
}
}
使用JDK动态代理对UserDao产生代理:
package com.rosinante.spring.demo1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 使用JDK动态代理对UserDap产生代理
* @author Administrator
*
*/
public class JdkProxy implements InvocationHandler {
//将被增强的对象传递到代理中
private UserDao userDao;
public JdkProxy(UserDao userDao){
this.userDao = userDao;
}
/**
* 产生UserDao代理的方法
* @return
*/
public UserDao createProxy(){
UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),this);
return userDaoProxy;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
//判断方法名是不是save
if("save".equals(method.getName())){
//增强
System.out.println("权限校验");
return method.invoke(userDao, args);
}
return method.invoke(userDao, args);
}
}
编写测试类:
package com.rosinante.spring.demo1;
import org.junit.Test;
public class SpringDemo1 {
@Test
//JDK动态代理
public void demo1(){
UserDao userDao = new UserDaoImpl();
//创建代理:
UserDao proxy = new JdkProxy(userDao).createProxy();
proxy.save();
proxy.update();
proxy.find();
proxy.delete();
}
}
运行结果如下:
2.Cglib动态代理
- Cglib:第三方开源代码生成类库,动态添加类的属性和方法。
使用cglib来增强方法:
创建一个CustomerDao类:
package com.rosinante.spring.demo2;
public class CustomerDao {
public void save(){
System.out.println("保存客户...");
}
public void find(){
System.out.println("查询客户...");
}
public void update(){
System.out.println("修改客户...");
}
public void delete(){
System.out.println("删除客户...");
}
}
使用cglib来增强CustomerDao中的save方法:
package com.rosinante.spring.demo2;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
/**
* Cglib动态代理
* @author Administrator
*
*/
public class CglibProxy implements MethodInterceptor{
private CustomerDao customerDao;
public CglibProxy(CustomerDao customerDao){
this.customerDao = customerDao;
}
/**
* 使用cglib产生代理的方法
*/
public CustomerDao createProxy(){
//1.创建Cglib的核心类对象
Enhancer enhancer = new Enhancer();
//2.设置父类
enhancer.setSuperclass(customerDao.getClass());
//3.设置回调:类似于InvocationHandler对象
enhancer.setCallback(this);
//4.创建代理对象
CustomerDao proxy= (CustomerDao) enhancer.create();
return proxy;
}
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
// TODO Auto-generated method stub
//判断方法是否为save
if("save".equals(method.getName())){
//增强
System.out.println("权限校验");
return methodProxy.invokeSuper(proxy, args);
}
return methodProxy.invokeSuper(proxy, args);
}
}
编写测试类:
package com.rosinante.spring.demo2;
import org.junit.Test;
public class SpringDemo2 {
@Test
/**
* cglib的测试
*/
public void demo1(){
CustomerDao customerDao = new CustomerDao();
CustomerDao proxy = new CglibProxy(customerDao).createProxy();
proxy.save();
proxy.update();
proxy.find();
proxy.delete();
}
}
运行结果如下:
三、Spring的AOP的开发(AspectJ的XML的方式)
1.Spring的AOP的简介
AOP思想最早是由AOP联盟组织提出的,Spring是使用这种思想最好的框架。
Spring的AOP有自己的实现方式(非常繁琐),AspectJ是一个AOP的框架,Spring引入AspectJ作为自身AOP的开发
Spring两套AOP开发方式
- Spring传统方式(弃用)
- Spring基于AspectJ的AOP的开发(使用)。
2、AOP的开发中的相关术语
- Joinpoint:连接点,可以被拦截到的点,(例如上例中的增删改查的方法,这些方法都可以被增强,便可称之为连接点)
- Pointcut:切入点,真正被拦截到的点。(如上例中只对save方法进行了增强,save便称之为切入点)
- Advice:通知、增强。(如上例中对save方法进行权限校验,权限校验(checkPri)的方法称为通知)
- Introduction:引介。类层面的增强。
- Target:被增强的对象。例如对UserDap增强,UserDao称为目标
- Weaving:织入,将通知应用(Advice)到目标(Target)的过程。(如上例将权限校验的方法的代码应用到UserDao的save方法上的过程)
- Proxy:代理对象
- Aspect:切面,多个通知和多个切入点的组合
四、Sprng的AOP的入门(AspectJ的XML方式)
1.创建web项目,引入jar包
2.引入Spring的配置文件
引入AOP的约束
<?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: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"> <!-- bean definitions here -->
</beans>
3.编写目标类
接口:
package com.rosinante.spring.demo3;
public interface ProductDao {
public void save();
public void update();
public void find();
public void delete();
}
实现类:
package com.rosinante.spring.demo3;
public class ProductDaoImpl implements ProductDao{
public void save() {
// TODO Auto-generated method stub
System.out.println("保存商品...");
}
public void update() {
// TODO Auto-generated method stub
System.out.println("修改商品...");
}
public void find() {
// TODO Auto-generated method stub
System.out.println("查询商品...");
}
public void delete() {
// TODO Auto-generated method stub
System.out.println("删除商品...");
}
}
配置目标类:
<!-- 配置目标对象:被增强的对象 -->
<bean id="productDao" class="com.rosinante.spring.demo3.ProductDaoImpl"></bean>
4.编写测试类
Spring整合Junit单元测试
package com.rosinante.spring.demo3;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* AOP入门
* @author Administrator
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo3 {
@Resource(name="productDao")
private ProductDao productDao;
@Test
public void demo1(){
productDao.save();
productDao.update();
productDao.delete();
productDao.find();
}
}
5.编写一个切面类
编写切面类:
package com.rosinante.spring.demo3;
/**
* 切面类
* @author Administrator
*
*/
public class MyAspectXML {
public void checkPri(){
System.out.println("权限校验...");
}
}
配置:
<!-- 将切面类交给Spring管理 -->
<bean id="myAspect" class="com.rosinante.spring.demo3.MyAspectXML"></bean>
6.通过AOP配置实现
<!-- 通过AOP的配置完成对目标类产生代理 -->
<aop:config>
<!-- 表达式配置哪些类的哪些方法需要进行增强 -->
<aop:pointcut expression="execution(* com.rosinante.spring.demo3.ProductDaoImpl.save(..))" id="pointcut1"/>
<!-- 配置切面 -->
<aop:aspect ref="myAspect">
<aop:before method="checkPri" pointcut-ref="pointcut1"/>
</aop:aspect>
</aop:config>
运行结果如下:
五、Spring中通知类型
1.前置通知:在目标方法执行之前进行操作
- 可以获取切入点信息
在MyAspectXML类checkPei方法中传入JoinPoint对象:
/**
* 前置通知
* @param joinPoint
*/
public void checkPri(JoinPoint joinPoint){
System.out.println("权限校验..."+joinPoint);
}
运行结果如下:
2.后置通知:在目标方法执行之后进行操作
在MyAspectXML类中编写后置通知方法:
- 后置通知举例:
/**
* 后置通知
*/
public void writeLog(){
System.out.println("日志记录...");
}
增强delete方法,配置后置通知:
<aop:pointcut expression="execution(* com.rosinante.spring.demo3.ProductDaoImpl.delete(..))" id="pointcut2"/>
<aop:after-returning method="writeLog" pointcut-ref="pointcut2"/>
运行结果如下:
- 后置通知可以获得方法返回值
将delete改为返回值类型为String的方法,以便于测试:
public interface ProductDao {
public void save();
public void update();
public void find();
public String delete();
}
public String delete() {
// TODO Auto-generated method stub
System.out.println("删除商品...");
return "delete";
}
配置returning:
<aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/>
在MyAspectXML类中的后置通知writeLog方法加入一个参数:
/**
* 后置通知
*/
public void writeLog(Object result){ //这里的对象名必须与配置文件中的一样
System.out.println("日志记录..."+result);
}
运行结果如下:
3.环绕通知:在目标方法执行之前和之后进行操作
- 环绕通知可以阻止目标方法的执行
在MyAspectXMl编写一个模拟性能监控的方法:
/**
* 性能监控
* @throws Throwable
*/
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("环绕前通知...");
Object obj = joinPoint.proceed();
System.out.println("环绕后通知...");
return obj;
}
配置环绕通知,增强update方法:
<aop:pointcut expression="execution(* com.rosinante.spring.demo3.ProductDaoImpl.update(..))" id="pointcut3"/>
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pointcut3"/>
4.异常抛出通知:在程序出现异常的时候进行的操作
为了验证异常通知,在find方法中加一个产生异常的语句:
public void find() {
// TODO Auto-generated method stub
System.out.println("查询商品...");
int i = 1/0;
}
在MyAspectXML中编写异常抛出通知方法:
/**
* 异常抛出
*/
public void afterThrowing(){
System.out.println("异常抛出通知...");
}
配置异常抛出通知:
<aop:pointcut expression="execution(* com.rosinante.spring.demo3.ProductDaoImpl.find(..))" id="pointcut4"/>
<!-- 异常抛出通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4"/>
运行结果如下:
- 异常抛出通知可以获得异常信息
在配置中加入throwing属性:
<!-- 异常抛出通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>
更改afterThrowing方法:
/**
* 异常抛出
*/
public void afterThrowing(Throwable ex){
System.out.println("异常抛出通知..."+ex.getMessage());
}
运行结果如下:
5.最终通知:无论代码是否有异常,总是会执行
在MyAspectXML类中编写最终通知方法:
/**
* 最终通知,相当于finally代码块中内容
*/
public void after(){
System.out.println("最终通知...");
}
配置最终通知,增强find方法:
<!-- 最终通知 -->
<aop:after method="after" pointcut-ref="pointcut4"/>
运行结果如下:
6.引介通知
六、Spring的切入点表达式写法
基于execution的函数完成的
语法:
【访问修饰符】方法返回值 包名.类名.方法名(参数)