Spring中AOP的配置从1.0到5.0的演进

最近在学习Spring稍微深入一点的东西,在这过程中发现虽然有很多关于各种AOP,IOC原理配置等的文章,但是都只是针对某一版本或者压根儿就没有标明版本的解析配置等。或许是我理解力不够,为了方便自己以后快速找到这些东西去看,还是自己记录下。

这里主要是记录下从Spring1.0到现在的5.0中AOP的配置方式,关于AOP原理和源码,暂先不解释。主要用作自己记录用,如果有错误的还请指出一起改正学习,免得误导别人,谢谢。

Spring1中AOP的配置

直接看Spring1.1.1的文档,里面都已经给出来了各种配置方式,更高版本的也都包含了这些,但是觉得看1.1.1的更纯粹一些。

使用ProxyFactoryBean创建AOP代理

使用ProxyFactoryBean的方式来配置AOP,是最基础的方法。这里我们用的是代理接口的方式,步骤大概是:

  1. 定义我们的业务接口和业务实现类。
  2. 定义通知类,就是我们要对目标对象进行增强的类。
  3. 定义ProxyFactoryBean,这里封装了AOP的功能。

这就是Spring中AOP的配置,这种方式简单明了,往下接着看示例代码。

我们的业务接口,LoginService:

package me.cxis.spring.aop;

/**
 * Created by cheng.xi on 2017-03-29 12:02.
 */
public interface LoginService {
    String login(String userName);
}

业务接口的实现类,LoginServiceImpl:

package me.cxis.spring.aop;

/**
 * Created by cheng.xi on 2017-03-29 10:36.
 */
public class LoginServiceImpl implements LoginService {

    public String login(String userName){
        System.out.println("正在登录");
        return "success";
    }
}

在登录前做一些操作的通知类,LogBeforeLogin:

//这里只是在登录方法调用之前打印一句话
public class LogBeforeLogin implements MethodBeforeAdvice {
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("有人要登录了。。。");
    }
}

配置文件,在Spring1.x中主要还是用xml的方式来配置:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!--业务处理类,也就是被代理的类-->
    <bean id="loginServiceImpl" class="me.cxis.spring.aop.LoginServiceImpl"/>

    <!--通知类-->
    <bean id="logBeforeLogin" class="me.cxis.spring.aop.LogBeforeLogin"/>

    <!--代理类-->
    <bean id="loginProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--要代理的接口-->
        <property name="proxyInterfaces">
            <value>me.cxis.spring.aop.LoginService</value>
        </property>
        <!--拦截器名字,也就是我们定义的通知类-->
        <property name="interceptorNames">
            <list>
                <value>logBeforeLogin</value>
            </list>
        </property>
        <!--目标类,就是我们业务的实现类-->
        <property name="target">
            <ref bean="loginServiceImpl"/>
        </property>
    </bean>
</beans>

测试方法,Main:

package me.cxis.spring.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by cheng.xi on 2017-03-29 10:34.
 */
public class Main {


    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:aop.xml");
        LoginService loginService = (LoginService) applicationContext.getBean("loginProxy");
        loginService.login("sdf");
    }
}

上面的例子是用的是代理接口的方式,关于代理类的方式这里不做介绍,代理类的方式需要依赖CGLIB。从上面的代码可以看到,这种方式使用AOP简单直观,也是我们理解Spring AOP原理的很好的入口,但是在使用的时候,可能会发现业务增多了之后,ProxyFactoryBean的配置也会增多,导致xml迅速变多。

另外这种方式的使用会把LoginService接口中所有的方法都代理了,也就是说每个方法都会被增强,如果不想被增强,还可以使用另外一种方式,配置Advisor。

使用ProxyFactoryBean和Advisor的方式创建AOP代理

使用Advisor配合,可以指定要增强的方法,不会把整个类中的所有方法都代理了。

这种方式跟上面的方式相比较,只是xml配置文件发生了变化,其他的代码都没有变,所以这里只列出了xml代码,其他的参照上面的代码。

xml配置:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!--业务处理类,也就是被代理的类-->
    <bean id="loginServiceImpl" class="me.cxis.spring.aop.advisor.LoginServiceImpl"/>

    <!--通知类-->
    <bean id="logBeforeLogin" class="me.cxis.spring.aop.advisor.LogBeforeLogin"/>

    <!--切面-->
    <bean id="loginAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice">
            <ref bean="logBeforeLogin"/>
        </property>
        <property name="pattern">
            <value>me.cxis.spring.aop.advisor.LoginServiceImpl.login*</value>
        </property>
    </bean>

    <!--代理类-->
    <bean id="loginProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="interceptorNames">
            <list>
                <value>loginAdvisor</value>
            </list>
        </property>
        <property name="target">
            <ref bean="loginServiceImpl"/>
        </property>
    </bean>
</beans>

可以看到这里我们多了Advisor,Advisor中可以使用正则表达式来匹配要增强的方法。

使用ProxyFactory编程的方式创建AOP代理

也可以使用直接代码的方式,不依赖xml文件,来创建AOP代理,直接看示例代码。

业务接口,LoginService:

package me.cxis.spring.aop.proxyfactory;

/**
 * Created by cheng.xi on 2017-03-29 12:02.
 */
public interface LoginService {
    String login(String userName);
}

业务接口实现类,LoginServiceImpl:

package me.cxis.spring.aop.proxyfactory;

/**
 * Created by cheng.xi on 2017-03-29 10:36.
 */
public class LoginServiceImpl implements LoginService {

    public String login(String userName){
        System.out.println("正在登录");
        return "success";
    }
}

通知类,LogBeforeLogin:

package me.cxis.spring.aop.proxyfactory;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

/**
 * Created by cheng.xi on 2017-03-29 10:56.
 */
public class LogBeforeLogin implements MethodBeforeAdvice {
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("有人要登录了。。。");
    }
}

测试方法:

package me.cxis.spring.aop.proxyfactory;

import org.springframework.aop.framework.ProxyFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by cheng.xi on 2017-03-29 10:34.
 */
public class Main {


    public static void main(String[] args) {
        ProxyFactory proxyFactory = new ProxyFactory();//创建代理工厂
        proxyFactory.setTarget(new LoginServiceImpl());//设置目标对象
        proxyFactory.addAdvice(new LogBeforeLogin());//前置增强

        LoginService loginService = (LoginService) proxyFactory.getProxy();//从代理工厂中获取代理
        loginService.login("x");
    }
}

这种方式跟上面使用ProxyFactoryBean的方式差不多,步骤也基本相同,不做过多解释。Spring不推荐这种方式。

使用autoproxy方式创建AOP

使用这种方式创建AOP代理,最主要的是可以使用一个配置来代理多个业务bean,也就是跟上面使用ProxyFactoryBean不同的地方,ProxyFactoryBean需要配置很多个ProxyFactoryBean配置,而autoproxy相对会很少。

使用autoproxy也有两种方式:BeanNameAutoProxyCreator和DefaultAdvisorAutoProxyCreator。两种方式的差别直接看代码。

BeanNameAutoProxyCreator方式

业务接口,LoginService:

package me.cxis.spring.aop.autoproxy;

/**
 * Created by cheng.xi on 2017-03-29 12:02.
 */
public interface LoginService {
    String login(String userName);
}

业务接口实现类,LoginServiceImpl:

package me.cxis.spring.aop.autoproxy;

/**
 * Created by cheng.xi on 2017-03-29 10:36.
 */
public class LoginServiceImpl implements LoginService {

    public String login(String userName){
        System.out.println("autoproxy:正在登录");
        return "success";
    }
}

通知类,LogBeforeLogin:

package me.cxis.spring.aop.autoproxy;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

/**
 * Created by cheng.xi on 2017-03-29 10:56.
 */
public class LogBeforeLogin implements MethodBeforeAdvice {
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("autoproxy:有人要登录了。。。");
    }
}

xml配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!--业务处理类,也就是被代理的类-->
    <bean id="loginService" class="me.cxis.spring.aop.autoproxy.LoginServiceImpl"/>

    <!--通知类-->
    <bean id="logBeforeLogin" class="me.cxis.spring.aop.autoproxy.LogBeforeLogin"/>

    <!--代理类-->
    <bean id="loginServiceProxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">

        <property name="interceptorNames">
            <list>
                <value>logBeforeLogin</value>
            </list>
        </property>
        <property name="beanNames">
            <value>loginService*</value>
        </property>
    </bean>
</beans>

测试方法,Main:

package me.cxis.spring.aop.autoproxy;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by cheng.xi on 2017-03-29 10:34.
 */
public class Main {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:aop-auto-proxy.xml");
        LoginService loginService = (LoginService) applicationContext.getBean("loginService");
        loginService.login("sdf");
    }
}

在xml配置中beanNames的值我们使用了通配符,也就是我们可以使用这一个BeanNameAutoProxyCreator来匹配很多个接口,在ProxyFactoryBean的方式中,我们则需要配置很多ProxyFactoryBean的配置。

另外也需要注意下在测试方法中我们对bean的调用方式,之前我们是调用代理类,现在我们直接调用的Bean。这种方式中也还是把业务类中的所有的方法都增强了。

DefaultAdvisorAutoProxyCreator方式

业务接口,LoginService:

package me.cxis.spring.aop.advisorautoproxy;

/**
 * Created by cheng.xi on 2017-03-29 12:02.
 */
public interface LoginService {
    String login(String userName);
}

业务接口实现类,LoginServiceImpl:

package me.cxis.spring.aop.advisorautoproxy;

/**
 * Created by cheng.xi on 2017-03-29 10:36.
 */
public class LoginServiceImpl implements LoginService {

    public String login(String userName){
        System.out.println("advisorautoproxy:正在登录");
        return "success";
    }
}

通知类,LogBeforeLogin:

package me.cxis.spring.aop.advisorautoproxy;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

/**
 * Created by cheng.xi on 2017-03-29 10:56.
 */
public class LogBeforeLogin implements MethodBeforeAdvice {
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("advisorautoproxy:有人要登录了。。。");
    }
}

配置文件xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!--业务处理类,也就是被代理的类-->
    <bean id="loginService" class="me.cxis.spring.aop.advisorautoproxy.LoginServiceImpl"/>

    <!--通知类-->
    <bean id="logBeforeLogin" class="me.cxis.spring.aop.advisorautoproxy.LogBeforeLogin"/>

    <bean id="logBeforeAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice">
            <ref bean="logBeforeLogin"/>
        </property>
        <property name="pattern">
            <value>me.cxis.spring.aop.advisorautoproxy.*</value>
        </property>
    </bean>

    <!--代理类-->
    <bean id="advisorAutoProxy" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
    </bean>
</beans>

测试方法:

package me.cxis.spring.aop.advisorautoproxy;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by cheng.xi on 2017-03-29 10:34.
 */
public class Main {


    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:aop-advisor-auto-proxy.xml");
        LoginService loginService = (LoginService) applicationContext.getBean("loginService");
        loginService.login("sdf");
    }
}

在这里的配置文件中我们使用了正则的方式来配置Advisor,这种可以匹配指定的方法,不需要把类中的所有的方法都增强了。

引介增强Introduction

上面所有AOP的代理都是对方法的增强,而引介增强则是对类的增强,所谓对类的增强就是,我是A类,实现了接口B,但是我没有实现接口C,那么通过引介增强的方式,我没有实现接口C,但是我可以调用C中的方法。

业务接口,LoginService:

package me.cxis.spring.aop.introduction;

/**
 * Created by cheng.xi on 2017-03-29 12:02.
 */
public interface LoginService {
    String login(String userName);
}

业务接口实现类:

package me.cxis.spring.aop.introduction;

/**
 * Created by cheng.xi on 2017-03-29 10:36.
 */
public class LoginServiceImpl implements LoginService {

    public String login(String userName){
        System.out.println("正在登录");
        return "success";
    }
}

另外一个业务接口,SendEmailService:

package me.cxis.spring.aop.introduction;

/**
 * Created by cheng.xi on 2017-03-30 23:45.
 */
public interface SendEmailService {
    void sendEmail();
}

增强,LogAndSendEmailBeforeLogin:

package me.cxis.spring.aop.introduction;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;

import java.lang.reflect.Method;

/**
 * Created by cheng.xi on 2017-03-29 10:56.
 */
public class LogAndSendEmailBeforeLogin extends DelegatingIntroductionInterceptor implements SendEmailService {


    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        System.out.println("有人要登录了。。。");
        return super.invoke(mi);
    }

    public void sendEmail() {
        System.out.println("发送邮件。。。。");
    }
}

xml配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!--业务处理类,也就是被代理的类-->
    <bean id="loginServiceImpl" class="me.cxis.spring.aop.introduction.LoginServiceImpl"/>

    <!--通知类-->
    <bean id="logAndSendEmailBeforeLogin" class="me.cxis.spring.aop.introduction.LogAndSendEmailBeforeLogin"/>

    <!--代理类-->
    <bean id="loginProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="interfaces">
            <value>me.cxis.spring.aop.introduction.SendEmailService</value>
        </property>
        <property name="interceptorNames">
            <list>
                <value>logAndSendEmailBeforeLogin</value>
            </list>
        </property>
        <property name="target">
            <ref bean="loginServiceImpl"/>
        </property>
        <property name="proxyTargetClass">
            <value>true</value>
        </property>
    </bean>
</beans>

测试方法:

package me.cxis.spring.aop.introduction;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by cheng.xi on 2017-03-29 10:34.
 */
public class Main {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:aop-introduction.xml");
        LoginServiceImpl loginService = (LoginServiceImpl) applicationContext.getBean("loginProxy");
        loginService.login("sdf");

        SendEmailService sendEmailService = (SendEmailService) loginService;
        sendEmailService.sendEmail();
    }
}

这样就可以了~

Spring2中AOP的配置

What's new in Spring 2.0?

  • 添加了基于schema的AOP支持。
  • 加入了AspectJ的支持,添加了@AspectJ注解。

上面就是AOP在2.0版本新增的特性,1.0的所有AOP配置方式在2.0中都支持,下面主要看看2.0中新增的一些方法。

使用@AspectJ的方式配置AOP代理

步骤大概如下:

  1. 定义我们的业务接口和业务实现类。
  2. 定义通知类,就是我们要对目标对象进行增强的类。使用@AspectJ注解。
  3. 启用@AspectJ支持。

还是看代码。

业务接口,LoginService:

package me.cxis.spring.aop;

/**
 * Created by cheng.xi on 2017-03-29 12:02.
 */
public interface LoginService {
    String login(String userName);
}

业务接口实现,LoginServiceImpl:

package me.cxis.spring.aop;

/**
 * Created by cheng.xi on 2017-03-29 10:36.
 */
public class LoginServiceImpl implements LoginService {

    public String login(String userName){
        System.out.println("正在登录");
        return "success";
    }
}

增强类,LogBeforeLogin,请注意这里面使用了注解:

package me.cxis.spring.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/**
 * Created by cheng.xi on 2017-03-29 10:56.
 */
@Aspect
public class LogBeforeLogin {

    @Pointcut("execution(* me.cxis.spring.aop.*.login(..))")
    public void loginMethod(){}

    @Before("loginMethod()")
    public void beforeLogin(){
        System.out.println("有人要登录了。。。");
    }
}

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="loginService" class="me.cxis.spring.aop.LoginServiceImpl"/>

    <!--Aspect-->
    <bean id="logBeforeLoginAspect" class="me.cxis.spring.aop.LogBeforeLogin">
    </bean>

</beans>

测试方法:

package me.cxis.spring.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by cheng.xi on 2017-03-29 10:34.
 */
public class Main {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:aop.xml");
        LoginService loginService = (LoginService) applicationContext.getBean("loginService");
        loginService.login("sdf");
    }
}

可以看下上面的配置文件,首先开启@AspectJ注解的支持,然后只需要声明一下业务bean和增强bean,其余的都不用做了,是不是比以前方便多了。以前的那些配置,全部都在增强类中用注解处理了。

使用自定义注解作为execution的表达式

上面使用普通的execution表达式来声明对那些方法进行增强,也可以使用注解的方式,其实就是把表达式换成了注解,只有添加了注解的方法才能被增强。

先定义一个注解UseAop:

package me.cxis.spring.aop.customannotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Created by cheng.xi on 2017-03-31 11:29.
 * 标注此注解的方法,需要使用AOP代理
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseAop {
}

业务接口,LoginService:

package me.cxis.spring.aop.customannotation;

/**
 * Created by cheng.xi on 2017-03-29 12:02.
 */
public interface LoginService {
    String login(String userName);
}

业务接口的实现,LoginServiceImpl:

package me.cxis.spring.aop.customannotation;

/**
 * Created by cheng.xi on 2017-03-29 10:36.
 */
public class LoginServiceImpl implements LoginService {

    @UseAop
    public String login(String userName){
        System.out.println("正在登录");
        return "success";
    }
}

这里业务实现类的方法使用了注解,表明这个方法需要使用AOP代理。

增强类,LogBeforeLogin:

@Aspect
public class LogBeforeLogin {
    
    @Pointcut("@annotation(me.cxis.spring.aop.customannotation.UseAop)")
    public void loginMethod(){}

    @Before("loginMethod()")
    public void beforeLogin(){
        System.out.println("有人要登录了。。。");
    }
}

这里是把表达式换成了我们定义的注解。

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="loginService" class="me.cxis.spring.aop.customannotation.LoginServiceImpl"/>

    <!--Aspect-->
    <bean id="logBeforeLoginAspect" class="me.cxis.spring.aop.customannotation.LogBeforeLogin">
    </bean>

</beans>

xml配置文件并没有改变。同样下面的测试方法也没有变。

测试方法:

package me.cxis.spring.aop.customannotation;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by cheng.xi on 2017-03-29 10:34.
 */
public class Main {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:aop-annotation.xml");
        LoginService loginService = (LoginService) applicationContext.getBean("loginService");
        loginService.login("sdf");
    }
}

使用基于schema的方式配置AOP代理

基于schema的方式配置,可以不使用注解,而是完全基于xml配置。

业务接口,实现类,增强如下:

package me.cxis.spring.aop.config;

/**
 * Created by cheng.xi on 2017-03-29 12:02.
 */
public interface LoginService {
    String login(String userName);
}

package me.cxis.spring.aop.config;

/**
 * Created by cheng.xi on 2017-03-29 10:36.
 */
public class LoginServiceImpl implements LoginService {

    public String login(String userName){
        System.out.println("正在登录");
        return "success";
    }
}

package me.cxis.spring.aop.config;

/**
 * Created by cheng.xi on 2017-03-29 10:56.
 */
public class LogBeforeLogin {

    public void beforeLogin(){
        System.out.println("有人要登录了。。。");
    }
}

可以看到上面三个类中,增强类只是一个普通的bean而已,所有的配置都在xml中。

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">

    <!--业务实现类-->
    <bean id="loginService" class="me.cxis.spring.aop.config.LoginServiceImpl"></bean>

    <!--增强类-->
    <bean id="logBeforeLogin" class="me.cxis.spring.aop.config.LogBeforeLogin"></bean>

    <aop:config>
        <aop:aspect id="loginAspect" ref="logBeforeLogin">
            <aop:pointcut expression="execution(* me.cxis.spring.aop.config.*.*(..))" id="beforeLoginPointCut"/>
            <aop:before method="beforeLogin" pointcut-ref="beforeLoginPointCut"/>
        </aop:aspect>
    </aop:config>

</beans>

可以类比下上面@AspectJ的方式,其实是一样的,只不过把注解的东西都放到了xml中。

测试方法:

package me.cxis.spring.aop.config;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by cheng.xi on 2017-03-29 10:34.
 */
public class Main {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:aop-config.xml");
        LoginService loginService = (LoginService) applicationContext.getBean("loginService");
        loginService.login("sdf");
    }
}

使用dtd方式

我们上面看到使用<aop:aspectj-autoproxy/>来启用@AspectJ的支持,这种方式使用的基于schema扩展的,如果想用原来的DTD模式也是可以的,使用AnnotationAwareAspectJAutoProxyCreator即可。

业务接口,业务实现类,增强类的代码都跟使用@AspectJ的方式配置AOP代理这一步的一样,除了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 class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>

    <!--业务实现-->
    <bean id="loginService" class="me.cxis.spring.aop.dtd.LoginServiceImpl"/>

    <!--Aspect-->
    <bean id="logBeforeLoginAspect" class="me.cxis.spring.aop.dtd.LogBeforeLogin">
    </bean>

</beans>

就是将<aop:aspectj-autoproxy/>替换成<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>,Spring文档上也有说明。

编程的方式使用@AspectJ

业务接口LoginService:

package me.cxis.spring.aop.programmatic;

/**
 * Created by cheng.xi on 2017-03-29 12:02.
 */
public interface LoginService {
    String login(String userName);
}

业务实现类:

package me.cxis.spring.aop.programmatic;

/**
 * Created by cheng.xi on 2017-03-29 10:36.
 */
public class LoginServiceImpl implements LoginService {

    public String login(String userName){
        System.out.println("正在登录");
        return "success";
    }
}

增强类:

package me.cxis.spring.aop.programmatic;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;


/**
 * Created by cheng.xi on 2017-03-29 10:56.
 */
@Aspect
public class LogBeforeLogin {

    @Pointcut("execution(* me.cxis.spring.aop.programmatic.*.login(..))")
    public void loginMethod(){}

    @Before("loginMethod()")
    public void beforeLogin(){
        System.out.println("有人要登录了。。。");
    }
}

测试方法:

package me.cxis.spring.aop.programmatic;

import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;

/**
 * Created by cheng.xi on 2017-03-29 10:34.
 */
public class Main {


    public static void main(String[] args) {

        AspectJProxyFactory factory = new AspectJProxyFactory();//创建代理工厂
        factory.setTarget(new LoginServiceImpl());//设置目标类
        factory.addAspect(LogBeforeLogin.class);//设置增强
        LoginService loginService = factory.getProxy();//获取代理
        loginService.login("xsd");
    }
}

基本跟使用xml方式的@Aspect的步骤差不多。

引介增强Introduction

感觉引介增强比1.0更强大了点,直接看代码,对比1.0的引介就知道了。

业务接口:

package me.cxis.spring.aop.introduction;

/**
 * Created by cheng.xi on 2017-03-29 12:02.
 */
public interface LoginService {
    String login(String userName);
}

接口实现:

package me.cxis.spring.aop.introduction;

/**
 * Created by cheng.xi on 2017-03-29 10:36.
 */
public class LoginServiceImpl implements LoginService {

    public String login(String userName){
        System.out.println("正在登录");
        return "success";
    }
}

另外一个业务接口:

package me.cxis.spring.aop.introduction;

/**
 * Created by cheng.xi on 2017-03-30 23:45.
 */
public interface SendEmailService {
    void sendEmail();
}

接口实现:

package me.cxis.spring.aop.introduction;

/**
 * Created by cheng.xi on 2017-03-31 13:46.
 */
public class SendMailServiceImpl implements SendEmailService {
    public void sendEmail() {
        System.out.println("发送邮件。。。。");
    }
}

增强,也就是把上面两个接口关联起来的地方:

package me.cxis.spring.aop.introduction;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;

/**
 * Created by cheng.xi on 2017-03-29 10:56.
 */
@Aspect
public class LogAndSendEmailBeforeLogin {

    @DeclareParents(value = "me.cxis.spring.aop.introduction.LoginServiceImpl",defaultImpl = SendMailServiceImpl.class)
    private SendEmailService sendEmailService;
}

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="loginService" class="me.cxis.spring.aop.introduction.LoginServiceImpl"/>

    <!--Aspect-->
    <bean id="logBeforeLoginAspect" class="me.cxis.spring.aop.introduction.LogAndSendEmailBeforeLogin">
    </bean>

</beans>

测试方法:

package me.cxis.spring.aop.introduction;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by cheng.xi on 2017-03-29 10:34.
 */
public class Main {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:aop-introduction.xml");
        LoginService loginService = (LoginService) applicationContext.getBean("loginService");
        loginService.login("sdf");

        SendEmailService sendEmailService = (SendEmailService) loginService;
        sendEmailService.sendEmail();
    }
}

具体的Introduction不做过多解释。另外上面也都是基于接口进行的代理,关于基于类的,这里不做说明。

上面是Spring2.0新增的关于AOP的配置的东西,1.0的方式在2.0中仍然适用。另外在Spring2.5中还增加了AspectJ的load-time织入的支持,也就是在类加载的时候织入。

Spring3,4,5中AOP的配置

Spring3,4,5基本就没在增加新的配置方式了,使用的方式基本都还是1.0和2.0中的方式,但是还会有很多的细节以及小特性添加,这里不能过多深入理解。暂先到这里。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,590评论 18 139
  • **** AOP 面向切面编程 底层原理 代理!!! 今天AOP课程1、 Spring 传统 AOP2、 Spri...
    luweicheng24阅读 1,351评论 0 1
  • AOP实现可分为两类(按AOP框架修改源代码的时机): 静态AOP实现:AOP框架在编译阶段对程序进行修改,即实现...
    数独题阅读 2,299评论 0 22
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,409评论 25 707
  • 萧萧入晚秋, 寂寂锁孤楼。 新妇相思苦, 征战几时休?
    鉬鈺阅读 234评论 16 21