什么是AOP?
AOP(Aspect-OrientedProgramming,面向切面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
大白话来说就是,面向对象解决了层次的问题,比如下级继承上级来增强上级等等。AOP解决了左右级问题,比如分别给一下不同的对象的某些方法加入不同的额外功能等等。特点是通过代理方式对目标类在运行期间织入功能。
Spring 动态代理中的基本概念
这些关键词配置会用到要记住
大概过程:先要定义一个切入点来设置在哪些方法进行拦截,然后写出切面并在这里对拦截到的连接点进行一些操作比如权限判断等,最后动态生成一个增强类。
Spring AOP增强类型
5中类型实现方法基本上差不多,
举个很简单的栗子:通过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"
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">
<!-- 目标对象,需要被增强的对象 -->
<bean id="tableServiceImpl" class="com.baidu.beans3.TableServiceImpl"></bean>
<!-- 配置增强方法,可以讲5种增强类型,写到一个类里,只要继承对应的接口即可-->
<bean id="myMethodBeforeAdvice" class="com.baidu.beans3.MyMethodBeforeAdvice"></bean>
<bean id="myAfterReturningAdvice" class="com.baidu.beans3.MyAfterReturningAdvice"></bean>
<bean id="myMethodInterceptor" class="com.baidu.beans3.MyMethodInterceptor"></bean>
<!-- 动态代理类,需要ProxyFactoryBean类或者自己写个继承类,几个关键的属性需要配置-->
<bean id="tableServicePro" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- target是Pro的一个继承过来的属性,定义对那些目标进行增强 -->
<property name="target" ref="tableServiceImpl"></property>
<!-- 生成的增强类 -->
<property name="proxyInterfaces" value="com.baidu.beans3.TableServiceI"></property>
<!-- 织入的增强方法,interceptorNames也是Pro的属性-->
<property name="interceptorNames" value="myMethodBeforeAdvice"></property>
</bean>
</beans>
三个增强类
参数类型主要有,目标类方法的返回值,目标类方法的名称,目标方法的参数等
public class MyAfterReturningAdvice implements AfterReturningAdvice{
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("后置通知,返回值是:"+returnValue);
}
}
public class MyMethodBeforeAdvice implements MethodBeforeAdvice{
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("前置增强");
}
}
public class MyMethodInterceptor implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("环绕通知1");
Object result = invocation.proceed();
System.out.println("环绕通知2");
return result;
}
}
测试:
public static void main(String[] args) {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("beans3.xml");
TableServiceI tableServiceI= (TableServiceI) applicationContext.getBean("tableServicePro");
tableServiceI.add();
}
大致过程:加载xml文件,获取动态代理类,动态代理类返回一个增强类,增强类调用方法时会执行织入的增强方法
问题:动态代理类多的时候会很麻烦,而且会拦截所有目标方法,目标方法执行任何方法都会被拦截。
解决办法:1.使用动态代理方式(配置好自动生成代理类),2.配置切入点(配置在哪些方法进行切入)
<?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"
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">
<!-- 目标对象,需要被增强的对象 -->
<bean id="tableServiceImpl" class="com.baidu.beans3.TableServiceImpl"></bean>
<!-- 配置增强,增强的类有哪些-->
<bean id="myMethodBeforeAdvice" class="com.baidu.beans3.MyMethodBeforeAdvice"></bean>
<bean id="myAfterReturningAdvice" class="com.baidu.beans3.MyAfterReturningAdvice"></bean>
<bean id="myMethodInterceptor" class="com.baidu.beans3.MyMethodInterceptor"></bean>
<bean id="regexpMethodPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- property是RegexpMethod下的匹配规则,可以多个 -->
<!-- add.*表示所有类型返回值,.add表示调用add的所有对象,*add表示所有add结尾的方法 -->
<property name="pattern" value=".*add.*"></property>
<!-- advice表示应用增强的类 -->
<property name="advice" ref="myMethodBeforeAdvice"></property>
</bean>
<!-- 动态生成代理 -->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<!-- 哪些目标要增强 -->
<property name="beanNames" value="*ServiceImpl"></property>
<!-- 哪些方法增强,怎么增强? -->
<property name="interceptorNames" value="regexpMethodPointcutAdvisor"></property>
</bean>
</beans>
也可以选择默认自动生成动态代理
<?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"
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">
<!-- 目标对象,需要被增强的对象 -->
<bean id="tableServiceImpl" class="com.baidu.beans3.TableServiceImpl"></bean>
<!-- 配置增强,增强的类有哪些-->
<bean id="myMethodBeforeAdvice" class="com.baidu.beans3.MyMethodBeforeAdvice"></bean>
<bean id="myAfterReturningAdvice" class="com.baidu.beans3.MyAfterReturningAdvice"></bean>
<bean id="myMethodInterceptor" class="com.baidu.beans3.MyMethodInterceptor"></bean>
<!-- 如何增强类的那么多方法,怎么识别哪些要增强? -->
<!-- 配置代理对象,通过代理对象让Spring帮忙做,这里只需要配置对应代理对象 -->
<bean id="regexpMethodPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- property是RegexpMethod下的匹配规则,可以多个 -->
<!-- add.*表示所有类型返回值,.add表示调用add的所有对象,*add表示所有add结尾的方法 -->
<property name="pattern" value=".*add.*"></property>
<!-- advice表示应用增强的类 -->
<property name="advice" ref="myMethodBeforeAdvice"></property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
但这样写效率还是不高,可以采用AspectJ进行配置
Spring新版已经将Aspect整合到Core包中了,AspectJ1.5之后可以直接用注解形式进行配置更方便
AspectJ通知类型
差不多就是切入点一样的概念
举个栗子:
配置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: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">
<!-- 开启AspectJ自动代理-->
<aop:aspectj-autoproxy />
<bean id="studentServiceImpl" class="com.baidu.beans4.StudentServiceImpl"></bean>
<bean class="com.baidu.beans4.MyAspect"></bean>
</beans>
代理类:切入点用缺省语句来,方便简单
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class MyAspect {
@Before("execution(* com.baidu.beans4..*(..))")
public void before(JoinPoint joinPoint) {
System.out.println("前置增强:"+joinPoint);
}
@AfterReturning(value="execution(* com.baidu.beans4..*(..))",returning="reval")
public void myAfterReturning(JoinPoint joinPoint,Object reval) {
System.out.println("后置增强:"+joinPoint+"--"+reval);
}
@Around(value="execution(* com.baidu.beans4..add*(..))")
public void myAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕增强1:"+joinPoint);
Object proceed = joinPoint.proceed();
System.out.println("环绕增强2:"+joinPoint+"--"+proceed);
}
@After(value="execution(* com.baidu.beans4..add*(..))")
public void myAfter(JoinPoint joinPoint) throws Throwable{
System.out.println("最终通知:"+joinPoint);
}
}
目标类
public class StudentServiceImpl implements StudentServiceI{
@Override
public void delete() {
System.out.println("delete");
}
@Override
public String add() {
System.out.println("add");
return "add";
}
}
接口类
public interface StudentServiceI {
void delete();
String add();
}
测试类
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("beans7.xml");
StudentServiceI studentServiceI=(StudentServiceI) applicationContext.getBean("studentServiceImpl");
studentServiceI.add();
studentServiceI.delete();
}
}
Aspect不仅可以通过注解,还能直接在Xml文件里配置
需要将代理类里的注解全部去掉:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspect {
public void before(JoinPoint joinPoint) {
System.out.println("前置增强:"+joinPoint);
}
public void myAfterReturning(JoinPoint joinPoint,Object reval) {
System.out.println("后置增强:"+joinPoint+"--"+reval);
}
public void myAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕增强1:"+joinPoint);
Object proceed = joinPoint.proceed();
System.out.println("环绕增强2:"+joinPoint+"--"+proceed);
}
public void myAfter(JoinPoint joinPoint) throws Throwable{
System.out.println("最终通知:"+joinPoint);
}
}
并在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: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">
<!-- 开启AspectJ自动代理-->
<aop:aspectj-autoproxy />
<bean id="studentServiceImpl" class="com.baidu.beans5.StudentServiceImpl"></bean>
<bean id="myAspect" class="com.baidu.beans5.MyAspect"></bean>
<aop:config>
<aop:aspect ref="myAspect">
<aop:pointcut expression="execution(* com.baidu.beans5..*(..))" id="point1"/>
<aop:before method="before" pointcut-ref="point1"/>
<aop:after-returning method="myAfterReturning" pointcut-ref="point1" returning="reval"/>
</aop:aspect>
</aop:config>
</beans>