·AOP的概述
·AOP Aspect Oriented Programing 面向切面编程
·AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
·Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码
左边是传统方式 右边是使用AOP
-AOP相关术语
- Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
- Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
- Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
- Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field.(一般不管 只关注方法层面的增强)
- Target(目标对象):代理的目标对象
- Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程.spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入(动态代理)
- Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类Aspect(切面):是切入点和通知(引介)的结合
·AOP底层实现
-
-JDK动态代理
-
-CGLIB实现代理
- ·对于不使用接口的业务类,无法使用JDK动态代理
- ·CGlib采用非常底层字节码技术,可以为一个类创建子类,解决无接口代理问题
-
-代理知识总结
- ·Spring在运行期,生成动态代理对象,不需要特殊的编译器
- ·Spring AOP的底层就是通过JDK动态代理或CGLib动态代理技术为目标Bean执行横向织入
- 1.若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
- 2.若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
- ·程序中应优先对接口创建代理,便于程序解耦维护
- ·标记为final的方法,不能被代理,因为无法进行覆盖
- -JDK动态代理,是针对接口生成子类,接口中方法不能使用final修饰
- -CGLib是针对目标类生产子类,因此类或方法不能使final的
- ·Spring只支持方法连接点,不提供属性连接
·Spring的传统AOP
-Spring AOP切面类型
-
- ·Advisor:代表一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截
- ·PointcutAdvisor:代表具有切点的切面,可以指定拦截目标类哪些方法
- ·IntroductionAdvisor:代表引介切面,针对引介通知而使用切面(不要求掌握)
-不带切入点的切面(Advisor案例)
MyBeforeAdvice
public class MyBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("这是前置通知"); } }
SpringDemo3
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringDemo3 { @Resource(name="studentDaoProxy") private StudentDao studentDao; @Test public void demo1Test(){ studentDao.save(); studentDao.delete(); studentDao.find(); studentDao.update(); } }
StudentDao
public interface StudentDao { public void find(); public void save(); public void update(); public void delete(); }
StudentDaoImpl
public class StudentDaoImpl implements StudentDao { @Override public void find() { System.out.println("学生查询"); } @Override public void save() { System.out.println("学生保存"); } @Override public void update() { System.out.println("学生修改"); } @Override public void delete() { System.out.println("学生删除"); } }
applicationContext.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--配置目标类=======================================--> <bean id="studentDao" class="com.windbag.aop.demo3.StudentDaoImpl"/> <!--前置通知类型=====================================--> <bean id="mybeforeadvice" class="com.windbag.aop.demo3.MyBeforeAdvice"/> <!--Spring的AOP 产生代理对象--> <bean id="studentDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!--配置目标类--> <property name="target" ref="studentDao"/> <!--实现的接口--> <property name="proxyInterfaces" value="com.windbag.aop.demo3.StudentDao"/> <!--采用拦截的名称--> <property name="interceptorNames" value="mybeforeadvice"/> </bean> </beans>
-ProxyFactoryBean中的配置
-
- -target:代理的目标对象
- -proxyInterfaces:代理要实现的接口
- ·如果多个接口可以使用以下格式赋值
- <list>
- <value></value>
- </list>
- -proxyTargetClass:是否对类代理而不是接口,设置为true时,使用CGLib代理
- -interceptorNames:需要织入目标的Advice
- -singleton:返回代理是否为单实例,默认为单例
- -optimize:当设置为true时,强制使用CGLib
-带切入点的切面
CustomerDao
public class CustomerDao { public void find(){ System.out.println("查询客户"); } public void save(){ System.out.println("保存用户"); } public void update(){ System.out.println("修改用户"); } public void delete(){ System.out.println("删除用户"); } }
MyAroundAdvice
public class MyAroundAdvice implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("环绕前通知================"); /*执行目标方法*/ Object obj = invocation.proceed(); System.out.println("环绕后通知================"); return obj; } }
SpringDemo4
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext2.xml") public class SpringDemo4 { /*@Resource(name="customerDao")*/ @Resource(name="customerDaoProxy") private CustomerDao customerDao; @Test public void demo1Test(){ customerDao.save(); customerDao.delete(); customerDao.find(); customerDao.update(); } }
applicationContext.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--配置目标类==================--> <bean id="customerDao" class="com.windbag.aop.demo4.CustomerDao"/> <!--配置通知====================--> <bean id="myAroundAdvice" class="com.windbag.aop.demo4.MyAroundAdvice"/> <!--一般的切面是使用通知作为切面的,因为要对目标类的某个方法进行增强就需要配置一个带有切入点的切面--> <bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!--pattern中配置正则表达式:.任意字符 *任意次数--> <!--<property name="pattern" value=".*"/>--> <!--<property name="pattern" value=".*save.*"/>--> <property name="patterns" value=".*save.*,.*delete.*"/> <property name="advice" ref="myAroundAdvice"/> </bean> <!--配置产生代理================--> <bean id="customerDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="customerDao"/> <property name="proxyTargetClass" value="true"/> <property name="interceptorNames" value="myAdvisor"/> </bean> </beans>
·Spring的传统AOP的自动代理
·前面的案例中,每个代理都是通过ProxyFactoryBean织入切面代理,在实际开发中,非常多的Bean每个都配置ProxyFactoryBean开发维护量巨大
·解决方案:自动创建代理
-BeanNameAutoProxy Creator 根据Bean名称创建代理
-DefaultAdvisorAutoProxy Creator 根据Advisor本身包含信息创建代理
-AnnotationAwareAspectJAutoProxyCreator 基于Bean中的AspectJ注解进行自动代理
-基于Bean名称的自动代理
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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"> <!--配置目标类--> <bean id="studentDao" class="com.windbag.aop.demo5.StudentDaoImpl"/> <bean id="customerDao" class="com.windbag.aop.demo5.CustomerDao"/> <!-- 配置增强--> <bean id="myBeforeAdvice" class="com.windbag.aop.demo5.MyBeforeAdvice"/> <bean id="myAroundAdvice" class="com.windbag.aop.demo5.MyAroundAdvice"/> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames" value="*Dao"/> <property name="interceptorNames" value="myBeforeAdvice"/> </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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--配置目标类--> <bean id="studentDao" class="com.windbag.aop.demo3.StudentDaoImpl"/> <bean id="customerDao" class="com.windbag.aop.demo6.CustomerDao"/> <!-- 配置增强--> <bean id="myBeforeAdvice" class="com.windbag.aop.demo3.MyBeforeAdvice"/> <bean id="myAroundAdvice" class="com.windbag.aop.demo6.MyAroundAdvice"/> <!--配置切面--> <bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="patterns" value="com.windbag.aop.demo3.StudentDaoImpl.save"/> <property name="advice" ref="myAroundAdvice"/> </bean> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"> </bean> </beans>