什么是AOP?
aop指的是面向切面编程,它本质上是一种技术。在企业开发中,很多应用的一些模块像日志,安全,事务等它们在多个应用中都存在,我们就可以将它们提取出来作为切面,这样能够让我们能加关注于业务层面,也减少了很多重复代码的编写。
AOP的几个专业名词
连接点(Joinpoint):目标对象中,所有可以增强的方法。
切入点(Pointcut):目标对象中,已经增强的方法
通知/增强(Advice):增强的代码。
目标对象(Target):被代理对象
织入(weaving):将通知应用到切入点的过程
代理(proxy):将通知织入到目标对象形成代理对象
切面(aspect):被抽取的公共模块,可能会横切多个对象
下面举个例子
public class Audience {
public void takeSeats() {
System.out.println("观众落座");
}
public void turnOffCellPhone() {
System.out.println("观众关闭手机");
}
public void applaud() {
System.out.println("好!");
}
public void demandRefund() {
System.out.println("退票");
}
public void watchPerformance(ProceedingJoinPoint joinpoint) {
try {
long start=System.currentTimeMillis();
joinpoint.proceed();
long end=System.currentTimeMillis();
System.out.println("表演用了"+(end-start)+"毫秒");
} catch (Throwable t) {
System.out.println("退票");
}
}
}
public class Performer {
public void perform() {
System.out.println("表演中");
}
}
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:context.xml");
Performer p = context.getBean(Performer.class);
p.perform();
首先观众类就是一个切面,它这其中的四个方法都是增强的代码,而perform就是切入点,performer就是目标对象,这几个方法将会根据定义在perform的前后调用。
spring的几种通知方法
1.前置通知(Before advice):在某连接点之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。
2.返回后通知(After returning advice):在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
3.抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。
4.返回通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
5.环绕通知(Around Advice):包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。 环绕通知是最常用的一种通知类型。
开始配置通知方法
首先在spring中使用aop需要这两个包
如果使用@Aspect需要添加
加一个aopalliance-1.0.jar的包
在spring中有xml文件和注解两种方式配置切面
首先在xml文件中配置
<aop:config>//顶层元素,一切aop元素都包含在它里面
<aop:aspect ref="audience">//定义切面
<aop:pointcut id="performance" expression="execution(* test.Performer.perform(..))"/>//定义切入点并取一个id
<aop:before pointcut-ref="performance" method="takeSeats"/>//前置通知
<aop:before pointcut-ref="performance" method="turnOffCellPhone"/>
<aop:after-returning pointcut-ref="performance" method="applaud"/>//后置通知
<aop:after-throwing pointcut-ref="performance" method="demandRefund"/>//异常通知
<aop:around pointcut-ref="performance" method="watchPerformance"/>//环绕通知
</aop:aspect>
</aop:config>
结果为
这就把我们的方法织入到了目标对象。
注解方式
注解总是能让过程变得简单,下面用注解实现切面
@Aspect
public class Audience {
@Pointcut("execution(* test.Performer.perform(..))")
public void performance() {
}
@Before("performance()")
public void takeSeats() {
System.out.println("观众落座");
}
@Before("performance()")
public void turnOffCellPhone() {
System.out.println("观众关闭手机");
}
@AfterReturning("performance()")
public void applaud() {
System.out.println("好!");
}
@AfterThrowing("performance()")
public void demandRefund() {
System.out.println("退票");
}
@Around("performance()")
public void watchPerformance(ProceedingJoinPoint joinpoint) {
try {
//System.out.println("观众落座");
//System.out.println("观众关闭手机");
long start=System.currentTimeMillis();
joinpoint.proceed();
long end=System.currentTimeMillis();
//System.out.println("好,鼓掌");
System.out.println("表演用了"+(end-start)+"毫秒");
} catch (Throwable t) {
System.out.println("退票");
}
}
xml文件
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>//自动为切面方法中匹配人的方法所在的类生成代理对象
结果
spring如何实现aop?
spring使用jdk动态代理和cglib动态代理来实现aop.jdk动态代理一般是使用反射来接收被代理的类,
被代理的类必须要实现接口。cglib动态代理的类实不实现接口都行,一般是没有实现接口的,cglib动态代理底层则是借助asm来实现的,可以在运行时动态生成某个类的子类,所以,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
可以认为,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效
demo实现
jdk动态代理中两个核心是InvocationHandler接口和Proxy类
public interface UserService { //定义一个被代理对象要实现的接口
public void save() ;
public void delete() ;
}
被代理对象类
public class UserServiceImp implements UserService{
@Override
public void save() {
System.out.println("保存数据");
}
@Override
public void delete() {
System.out.println("删除数据");
}
}
实现动态代理类MyInvocationHandler
public class MyInvocationHandler implements InvocationHandler{
private Object target;
public MyInvocationHandler(Object target) {
super();
this.target = target;
}
public MyInvocationHandler() {
super();
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置通知");
Object result = method.invoke(target, args);//这个语句用来执行目标类中的方法
System.out.println("后置通知");
return result;
}
}
测试main
public class Test {
public static void main(String[] args) {
UserService us=new UserServiceImp() ;
MyInvocationHandler handler=new MyInvocationHandler(us); //为实现类创建一个代理实例
UserService usproxy=(UserService)Proxy.newProxyInstance(us.getClass().getClassLoader(),us.getClass().getInterfaces(),handler); //动态生成代理对象
usproxy.save();
usproxy.delete();
}
}
结果为
可以看出已经实现了AOP的功能
cglib动态代理
public class UserService { //被代理类不用实现接口
public void save() {
System.out.println("保存数据");
}
public void delete() {
System.out.println("删除数据");
}
}
实现动态代理类,cglib代理需要实现MethodInterceptor接口,并实现intercept方法
public class CglibProxy implements MethodInterceptor {
public Object intercept(Object object, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("前置通知");
proxy.invokeSuper(object, args);//执行目标类方法
System.out.println("后置通知");
return null;
}
}
工厂类,生成增强过的目标类,Enhancer 就是完成这一功能的
public class Factory {
public static UserService getInstance(CglibProxy proxy) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(proxy);
UserService us = (UserService) enhancer.create();
return us;//此时返回的已经是增强过的目标类对象
}
}
测试
public class Test {
public static void main(String[] args)
{
CglibProxy proxy=new CglibProxy();
UserService us=Factory.getInstance(proxy);
us.save();
us.delete();
}
}
结果