您的位置:首页 > 编程语言 > Java开发

AOP学习笔记(二)——Spring AOP

2017-05-20 11:07 435 查看
1、什么是AOP
     AOP(Aspect-Oriented Programming),面向切面编程,是OOP的补充,主要用于将与业务逻辑无关的代码剥离,例如:性能监控、日志记录、权限控制等。
2、实现方案
     静态代理
     JDK动态代理
     CGLIB动态代理
3、现有AOP框架:Spring AOP
     前置增强、后置增强、环绕增强(编程式)
     接口:
public interface Greeting {
public void say();
}

     实现类:
public class GreetingImpl implements Greeting{
public void say(){
System.out.println("Hello");
}
}

     前置增强类:
public class GreetingBeforeAdvice implements MethodBeforeAdvice {

@Override
public void before(Method method, Object[] args, Object target) throws Throwable{
System.out.println("Before");
}
}

     后置增强类:
public class GreetingAfterAdvice implements AfterReturningAdvice {

@Override
public void afterReturning(Object result, Method method, Object[] args, Object target) throws Throwable{
System.out.println("After");
}

}

     测试:
public class Test {
public static void main(String[] args){
ProxyFactory proxyFactory = new ProxyFactory(); //创建代理工厂
proxyFactory.setTarget(new GreetingImpl()); //目标对象
proxyFactory.addAdvice(new GreetingBeforeAdvice()); //前置增强
proxyFactory.addAdvice(new GreetingAfterAdvice()); //后置增强
Greeting greeting = (Greeting) proxyFactory.getProxy(); //从代理工厂中获取代理
greeting.say(); //调用代理方法
}
}

     当然我们也可以只定义一个增强类,同时实现前置增强和后置增强:
public class GreetingBeforeAndAfterAdvice implements MethodBeforeAdvice, AfterReturningAdvice {

@Override
public void before(Method method, Object[] args, Object target) throws Throwable{
System.out.println("Before");
}

@Override
public void afterReturning(Object result, Method method, Object[] args, Object target) throws Throwable{
System.out.println("After");
}

}

     这样,我们在使用时就可以这样写:
public class Test {
public static void main(String[] args){
ProxyFactory proxyFactory = new ProxyFactory(); //创建代理工厂
proxyFactory.setTarget(new GreetingImpl()); //目标对象
proxyFactory.addAdvice(new GreetingBeforeAndAfterAdvice()); //前置增强 和 后置增强
Greeting greeting = (Greeting) proxyFactory.getProxy(); //从代理工厂中获取代理
greeting.say(); //调用代理方法
}
}

     在上面定义的GreetingBeforeAndAfterAdvice类,实现了两个接口,springAOP提供了环绕增强,我们只需要实现一个接口:
public class GreetingAroundAdvice implements MethodInterceptor {

@Override
public Object invoke(MethodInvocation invocation) throws Throwable{
before();
Object result = invocation.proceed();
after();
return result;
}

public void before(){
System.out.println("Before");
}

public void after(){
System.out.println("After");
}
}

         使用时:
public class Test {
public static void main(String[] args){
ProxyFactory proxyFactory = new ProxyFactory(); //创建代理工厂
proxyFactory.setTarget(new GreetingImpl()); //目标对象
proxyFactory.addAdvice(new GreetingAroundAdvice()); //环绕增强
Greeting greeting = (Greeting) proxyFactory.getProxy(); //从代理工厂中获取代理
greeting.say(); //调用代理方法
}
}

      以上即为Sping AOP的编程式基本用法,下面介绍声明式的用法:
     首先添加配置文件:
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
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.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" default-lazy-init="true">

<!-- 开启组件自动扫描 -->
<context:component-scan base-package="proxy" />

<bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interfaces" value="proxy.Greeting" /> <!-- 需要代理的接口 -->
<property name="target" ref="greetingImpl"/>  <!-- 接口实现类 -->
<property name="interceptorNames" value="greetingAroundAdvice"/> <!-- 增强类 -->
</bean>

</beans>

     添加注解:
@Component
public class GreetingAroundAdvice implements MethodInterceptor {
...
}

@Component
public class GreetingImpl implements Greeting{
...
}

     使用:
public class Test {
public static void main(String[] args){
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-context.xml");
Greeting greeting = (Greeting) ctx.getBean("greetingProxy");
greeting.say();
}
}

     除了上面介绍了三类增强,还有两类增强:
     抛出增强:
@Component
public class GreetingThrowAdvice  implements ThrowsAdvice{
public void afterThrowing(Method method, Object[] args, Object target, Exception e){
System.out.println("-------------Throw Exception------------------");
System.out.println("Target Class: " + target.getClass().getName());
System.out.println("Method Name: " + method.getName());
System.out.println("Exception Message: " + e.getMessage());
System.out.println("----------------------------------------------");
}
}

     引入增强:上面介绍的四种增强是对方法的增强,叫Weaving(织入),这类叫Introduction(引入)。
     定义一个新的接口:
public interface Apology {
void saySorry();
}

     引入增强类:
@Component
public class GreetingIntroAdvice extends DelegatingIntroductionInterceptor implements Apology {

@Override
public Object invoke(MethodInvocation invocation) throws Throwable{
return super.invoke(invocation);
}

@Override
public void saySorry(){
System.out.println("sorry");
}

}

     配置:
<bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interfaces" value="proxy.Apology" /> <!-- 需要动态实现的接口 -->
<property name="target" ref="greetingImpl"/>  <!-- 目标类 -->
<property name="interceptorNames" value="greetingIntroAdvice"/> <!-- 引入增强类 -->
<property name="proxyTargetClass" value="true"/> <!-- 代理目标类(默认为false,代理接口) -->
</bean>

     注意上面的 proxyTargetClass ,表明是否代理目标类,false指代理接口,true指代理类,即使用CGLIB代理。
     使用:
public class Test {
public static void main(String[] args){
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-context.xml");
Greeting greeting = (Greeting) ctx.getBean("greetingProxy");
greeting.say();

Apology apology = (Apology) greeting;
apology.saySorry();
}
}

     AOP框架可以理解为一个拦截器,上面提到的增强类,会对被代理类的所有方法都进行拦截。如果要对特定的方法进行拦截,这个时候就要用到一个重要的工具——Advisor(切面),Advisor(切面)= Advice(增强)+ Pointcut(切点)
     首先改造一下原有的接口Greeting:
public interface Greeting {
void say();
void goodMorning();
}

     实现类:
@Component
public class GreetingImpl implements Greeting{

public void say(){
System.out.println("Hello");
}

public void goodMorning(){
System.out.println("Good Morning!");
}

}

     配置文件:
<!-- 开启组件自动扫描 -->
<context:component-scan base-package="proxy" />

<!-- 配置一个切面 -->
<bean id="greetingAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="greetingAroundAdvice"/> <!-- 增强 -->
<property name="pattern" value="proxy.GreetingImpl.good.*"/> <!-- 切点(正则表达式) -->
</bean>

<!-- 配置一个切面 -->
<bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="greetingImpl"/>  <!-- 目标类 -->
<property name="interceptorNames" value="greetingAdvisor"/> <!-- 切面 -->
<property name="proxyTargetClass" value="true"/> <!-- 代理目标类(默认为false,代理接口) -->
</bean>

     使用:
public class Test {
public static void main(String[] args){
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-context.xml");
Greeting greeting = (Greeting) ctx.getBean("greetingProxy");
greeting.say();
greeting.goodMorning();
}
}

     随着项目的扩大,代理的配置会越来越多,这时候可以让spring AOP框架为我们自动生成代理。
     Spring AOP:自动代理(扫描Bean名称)
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="*Impl"/> <!-- 只为后缀是“Impl”的Bean生成代理 -->
<property name="interceptorNames" value="greetingAroundAdvice"/> <!-- 增强 -->
<property name="optimize" value="true"/> <!-- 是否对代理生成策略优化 -->
</bean>

     注意上面的配置项:optimize,若为true,则可为代理生成策略优化(默认是false)。也就是说,如果该类有接口,则使用JDK动态代理;如果没有接口就使用CGLIB代理。
     Spring AOP:自动代理(扫描切面配置)
<!-- 配置一个切面 -->
<bean id="greetingAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="greetingAroundAdvice"/> <!-- 增强 -->
<property name="pattern" value="proxy.GreetingImpl.good.*"/> <!-- 切点(正则表达式) -->
</bean>

<!-- 这个类会自动烧苗所有的切面类,并为其自动生成代理 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
<property name="optimize" value="true"/>
</bean>

     上面的配置中,我们仍然无法解决当项目扩大时,切面的配置增多的问题。这个时候就需要用到AspectJ
4、Spring + AspectJ
     1) 基于注解:通过AspectJ execution 表达式拦截方法
@Aspect
@Component
public class GreetingAspect {

@Around("execution(* proxy.GreetingImpl.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
before();
Object result = pjp.proceed();
after();
return result;
}

public void before(){
System.out.println("Before");
}

public void after(){
System.out.println("After");
}
}

     配置:
<context:component-scan base-package="proxy" />

<aop:aspectj-autoproxy proxy-target-class="true"/>

     2) 基于注解:通过AspectJ @annotation 表达式拦截方法
     首先定义一个注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Tag {
}

     将注解加到要拦截的方法上:
@Tag
public void say(){
System.out.println("Hello");
}

     3)基于注解:引入增强
@Aspect
@Component
public class GreetingAspect {

@DeclareParents(value = "proxy.GreetingImpl", defaultImpl=ApologyImpl.class)
private Apology apology;
}

     4)基于配置:
<bean id="greetingImpl" class="proxy.GreetingImpl"/>

<bean id="greetingAspect" class="proxy.GreetingAspect"/>

<aop:config>
<aop:aspect ref="greetingAspect">
<aop:around method="around" pointcut="execution(* proxy.GreetingImpl.*(..))"/>
</aop:aspect>
</aop:config>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: