Spring AOP入门
为什么要使用AOP
public class Dog {
public void run() {
System.out.println("驯兽师发出命令!")
System.out.println("小狗开始跑!");
System.out.pringln("驯兽师给与奖励");
}
public void jump() {
System.out.println("驯兽师发出命令!")
System.out.println("小狗开始跳!");
System.out.pringln("驯兽师给与奖励");
}
}
仔细看上面的代码,我们可以看出在run方法和jump方法中,存在一些相同的内容(驯兽师发出命令和给与奖励),这些内容并不能完全进行抽象,即不能按照OOP编程思想进行处理。类似这样的情况同样会出现在我们编程中的很多地方,但是这样的情况该如何解决呢?这就引入了AOP编程思想。
AOP: AOP为Aspect Oriented Programming的缩写,即面向切面编程(也叫面向方面),是一种可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
(一)AOP的底层实现
JDK动态代理和Cglib动态代理
首先:JDK动态代理
案例:
创建userSercice
public interface UserService {
public void save();
public void update();
public void delete();
public void find();
}
创建userServiceImpl
public class UserServiceImpl implements UserService {
public void save() {
System.out.println("保存用户...");
}
public void update() {
System.out.println("修改用户...");
}
public void delete() {
System.out.println("删除用户...");
}
public void find() {
System.out.println("查询用户...");
}
创建JDK动态代理的类
public class MyJdbProxy implements InvocationHandler {
private UserService userService;
public MyJdbProxy(UserService userService){
this.userService = userService;
}
public UserService createProxy(){
// 生成UserSErvice的代理:
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(), userService.getClass()
.getInterfaces(), this);
return userServiceProxy;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 判断是否是save方法:
if("save".equals(method.getName())){
// 增强:
System.out.println("权限校验===========");
return method.invoke(userService, args);
}
//判断是不是方法delete
else if ("delete".equals(method.getName())){
System.out.println("删除开始");
return method.invoke(userService,args);
}
return method.invoke(userService, args);
}
}
创建测试类
public class SpringDemo {
@Test
// 没有代理的时候的调用方式
public void demo1() {
// 创建目标对象
UserService userService = new UserServiceImpl();
userService.save();
userService.update();
userService.delete();
userService.find();
}
@Test
// 使用代理
public void demo2() {
// 创建目标对象
UserService userService = new UserServiceImpl();
UserService proxy = new MyJdbProxy(userService).createProxy();
proxy.save();
proxy.update();
proxy.delete();
proxy.find();
}
}
那个Cglib动态代理只是略有不同在代理类的不同
public class MyCglibProxy implements MethodInterceptor {
private CustomerService customerService;
public MyCglibProxy(CustomerService customerService){
this.customerService = customerService;
}
public CustomerService createProxy(){
// 创建核心类:
Enhancer enhancer = new Enhancer();
// 设置父类:
enhancer.setSuperclass(customerService.getClass());
// 设置回调:
enhancer.setCallback(this);
// 创建代理:
CustomerService customerServiceProxy = (CustomerService) enhancer.create();
return customerServiceProxy;
}
public Object intercept(Object proxy, Method method, Object[] arg,
MethodProxy methodProxy) throws Throwable {
if("delete".equals(method.getName())){
Object obj = methodProxy.invokeSuper(proxy, arg);
System.out.println("日志记录==========");
return obj;
}
return methodProxy.invokeSuper(proxy, arg);
}
}
AOP的底层就是这样实现的。
(二)AOP正式介绍
AOP的相关术语
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义. Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程. spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面): 是切入点和通知(引介)的结合
通知类型
前置通知 :在目标方法执行 之前.
后置通知 :在目标方法执行 之后
环绕通知 :在目标方法执行
前和后异常抛出通知:在目标方法执行现 异常的时候 执行
最终通知 :无论目标方法是否出现异常 最终通知都会 执行 .
切入点表达式
execution( 表达式 ) 表达式 : [方法访问修 饰符 ] 方法返回值 方法返回值 包名 .类名 .方法名 (方法的参数方法的参数 )
public * cn.itcast.spring.dao..(..)
* cn.itcast.spring.dao...(…)
* cn.itcast.spring.dao.UserDao+.(..) *
实例:
编写一个切面类
/**
* 自定义切面类:
*
*/
@Aspect
public class MyAspectAnno {
@Before(value="execution(* com.txp.dao.UserSevice+.save(..))")
public void before(JoinPoint joinPoint){
System.out.println("前置通知============"+joinPoint);
}
@AfterReturning(value="execution(* com.txp.dao.UserSevice+.update(..))",returning="result")
public void afterReturing(Object result){
System.out.println("后置通知============"+result);
}
@Around(value="execution(* com.txp.dao.UserSevice+.delete(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("环绕前通知==========");
Object obj = joinPoint.proceed();
System.out.println("环绕后通知==========");
return obj;
}
@AfterThrowing(value="MyAspectAnno.myPointcut1()",throwing="e")
public void afterThrowing(Throwable e){
System.out.println("异常抛出通知========="+e.getMessage());
}
@After(value="MyAspectAnno.myPointcut1()")
public void after(){
System.out.println("最终通知===========");
}
@Pointcut(value="execution(* com.txp.dao.UserSevice+.find(..))")
private void myPointcut1(){}
}
beans.xml文件改造
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<!-- 使用注解完成AOP的开发 -->
<aop:aspectj-autoproxy/>
<!-- 目标对象 -->
<bean id="userService" class="com.txp.dao.UserServiceImpl"/>
<!-- 配置切面 -->
<bean id="myAspectAnno" class="com.txp.aspectanno.MyAspectAnno"/>
</beans>
其他文件同上
测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:beans.xml")
public class SpringDemo1 {
@Resource(name = "userService")
private UserSevice useerService;
@Test
public void demo1() {
useerService.save();
useerService.update();
useerService.delete();
useerService.find();
}
}