AOP的思想是:纵向重复,横向抽取。
(一)AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
(二)AOP名次
1、连接点(joinpoint)
目标对象中所有可以增强的方法,被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
2、切入点(pointcut)
目标对象,已经增强的方法
3、通知|增强(advice)
增强的代码,所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类
4、目标对象
被代理的目标对象
5、织入(weave)
将应用通知到切入点的过程
6、引入(introduction)
在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
7、代理(Proxy)
将通知织入到目标对象中之后,形成代理对象。
8、切面(aspect)
切入点+通知
(三)Spring中的aop概念以及原理
spring能够为容器中的管理的对象生成动态代理对象。
以前我们用动态代理,我们需要自己调用下面这个方法,Proxy.newProxyInstance(xx,xx,xx)生成代理对象。Spring能帮我们自动生成代理。
原理:动态代理(优先)
被代理对象必须要实现接口,才能产生代理对象。如果没有接口将不能使用动态代理技术。
calib代理(没有接口)
第三方代理技术,cglib代理.可以对任何类生成代理.代理的原理是对目标对象进行继承代理. 如果目标对象被final修饰.那么该类无法被cglib代理.
(四)导包
(五)准备目标对象
package cn.itcast.service;
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("添加");
}
@Override
public void delete() {
System.out.println("删除!");
}
@Override
public void update() {
System.out.println("更新!");
}
@Override
public void find() {
System.out.println("查找!");
}
}
(六)准备通知
package cn.justdoit.aopDemo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
public class MyAdvice {
public void before(){
System.out.println("前置");
}
public void afterReturning(){
System.out.println("后置(异常不会调用)!!");
}
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前!!");
Object proceed = pjp.proceed();
System.out.println("环绕后!!");
return proceed;
}
public void afterException(){
System.out.println("异常!");
}
public void after(){
System.out.println("后置(异常也会调用)!!");
}
}
(七).配置进行织入,将通知织入目标对象中
1、基于xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" 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-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
<!-- 准备工作: 导入aop(约束)命名空间 -->
<!-- 1.配置目标对象 -->
<bean name="userService" class="cn.itcast.service.UserServiceImpl" ></bean>
<!-- 2.配置通知对象 -->
<bean name="myAdvice" class="cn.itcast.d_springaop.MyAdvice" ></bean>
<!-- 3.配置将通知织入目标对象 -->
<aop:config>
<!-- 配置切入点
public void cn.itcast.service.UserServiceImpl.save()
void cn.itcast.service.UserServiceImpl.save()
* cn.itcast.service.UserServiceImpl.save()
* cn.itcast.service.UserServiceImpl.*()
* cn.itcast.service.*ServiceImpl.*(..)
* cn.itcast.service..*ServiceImpl.*(..)
-->
<aop:pointcut expression="execution(* cn.itcast.service.*ServiceImpl.*(..))" id="pc"/>
<aop:aspect ref="myAdvice" >
<!-- 指定名为before方法作为前置通知 -->
<aop:before method="before" pointcut-ref="pc" />
<!-- 后置 -->
<aop:after-returning method="afterReturning" pointcut-ref="pc" />
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pc" />
<!-- 异常拦截通知 -->
<aop:after-throwing method="afterException" pointcut-ref="pc"/>
<!-- 后置 -->
<aop:after method="after" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
</beans>
2、基于注解
package cn.justdoit.aopDemo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class MyAdvice {
@Pointcut("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public void pc(){}
@Before("MyAdvice.pc()")
public void before(){
System.out.println("前置");
}
@AfterReturning("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public void afterReturning(){
System.out.println("后置(异常不会调用)!!");
}
@Around("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前!!");
Object proceed = pjp.proceed();
System.out.println("环绕后!!");
return proceed;
}
@AfterThrowing("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public void afterException(){
System.out.println("异常!");
}
@After("execution(* cn.itcast.service.*ServiceImpl.*(..))")
public void after(){
System.out.println("后置(异常也会调用)!!");
}
}
(八)测试
package cn.justdoit.aopDemo;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import cn.itcast.bean.User;
import cn.itcast.service.UserService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:cn/justdoit/aopDemo/applicationContext.xml")
public class testDemo {
@Resource(name="userService")
private UserService us;
@Test
public void fun1(){
us.save();
}
}
(九)结果