spring顾问包装通知

前边说到了顾问就是通知,没有实践,这里就实践一下,证明一下。

虽然可以说顾问就是通知,但是他们还是有的一定的区别的,通知是将目标类中所有的方法都进行的增强,而顾问却可以指定到特定的方法上,也就是说顾问的功能更加强大一些

而包装的方式常用的有两种,一种是基于名字的(方法),一种是基于正则的(方法),他们都是通过目标类的方法名进行的包装。

创建增强类

 1 /**
 2  * 增强类
 3  */
 4 public class Advice implements MethodBeforeAdvice, AfterReturningAdvice {
 5     @Override
 6     public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
 7         System.out.println("我是后置增强");
 8     }
 9 
10     @Override
11     public void before(Method method, Object[] objects, Object o) throws Throwable {
12         System.out.println("我是前置增强");
13     }
14 }

创建service接口

1 public interface DoSome {
2     void dosome();
3     void say();
4 }

创建实现类

public class DoSomeImpl implements DoSome{
    public void dosome(){
        System.out.println("我是service层");
    }

    @Override
    public void say() {
        System.out.println("你好,bdqn");
    }
}

由于两种增强方式的service层和增强类都是一样的这里我只写一次

第一种根据方法名包装

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 5     <!--创建service层的bean-->
 6     <bean id="service" class="service.DoSomeImpl"></bean>
 7     <!--创建通知的bean-->
 8     <bean id="advice" class="advice.Advice"></bean>
 9     <!--顾问包装通知-->
10     <bean id="advisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
11         <property name="advice" ref="advice"></property>
12         <property name="mappedNames" value="say"></property>
13     </bean>
14     <!--创建代理工厂bean,并注入属性-->
15     <bean id="factoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
16         <!--注入service实现类的bean-->
17         <property name="target" ref="service"/>
18         <!--注入通知的bean,注意这里不使用ref注入,只能是value属性,属性为avcisor 的bean的id属性-->
19         <property name="interceptorNames" value="advisor"/>
20     </bean>
21 </beans>

创建测试类

public class Dome {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");
        DoSome proxyFactory = (DoSome) context.getBean("factoryBean");//这里注入的是ProxyFactoryBean的id属性值
        proxyFactory.say();
        System.out.println(">>>>>>>>>>>两个方法的分割线>>>>>>>>>>>>");
        proxyFactory.dosome();
    }
}

第二种基于正则的方式

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 5     <!--创建service层的bean-->
 6     <bean id="service" class="service.DoSomeImpl"></bean>
 7     <!--创建通知的bean-->
 8     <bean id="advice" class="advice.Advice"></bean>
 9     <!--顾问包装通知-->
10     <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
11         <property name="advice" ref="advice"></property>
12         <property name="pattern" value=".*say.*"/>
13     </bean>
14     <!--创建代理工厂bean,并注入属性-->
15     <bean id="factoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
16         <!--注入service实现类的bean-->
17         <property name="target" ref="service"/>
18         <!--注入通知的bean,注意这里不使用ref注入,只能是value属性,属性为通知类bean的id属性-->
19         <property name="interceptorNames" value="advisor"/>
20     </bean>
21 </beans>

测试类与第一种方式相同,结果自行验证,需要的pom节点

<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.0.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.1.0.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.1.0.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-expression -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>5.1.0.RELEASE</version>
        </dependency>
        <!--以上4个是spring的核心-->
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.2</version>
        </dependency>

每次写一个通知都需要手动去添加一个代理工厂也是很烦人的,那自然有简单的方式,只需要配置一次即可以了

基于id名的代理生成器:

<?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">
    <!--创建service层的bean-->
    <bean id="service" class="service.DoSomeImpl"></bean>
    <!--创建通知的bean-->
    <bean id="advice" class="advice.Advice"></bean>
    <!--基于方法名的包装-->
    <bean id="advisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
        <property name="advice" ref="advice"></property>
        <property name="mappedName" value="say"></property>
    </bean>
    <!--基于方法名的代理生成器-->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="beanNames" value="service"></property>
        <property name="interceptorNames" value="advisor"/>
    </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">
    <!--创建service层的bean-->
    <bean id="service" class="service.DoSomeImpl"></bean>
    <!--创建通知的bean-->
    <bean id="advice" class="advice.Advice"></bean>
    <!--顾问包装通知-->
    <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice" ref="advice"></property>
        <property name="pattern" value=".*say.*"/>
    </bean>
    <!--基于方法名的代理生成器-->
   <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
       <property name="interceptorNames" value="service"/>
       <property name="beanName" value="advisor"/>
   </bean>
</beans>

由于代理工厂也被代理了,所以没有了代理工厂的id这里测试类需要做一定的改变

public class Dome {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");
        DoSome proxyFactory = (DoSome) context.getBean("service");
        proxyFactory.say();
        System.out.println(">>>>>>>>>>>两个方法的分割线>>>>>>>>>>>>");
        proxyFactory.dosome();
    }
}

自动代理生成器推荐使用第一种,第二种容易出bug,因为它默认会为xml文件中的所有bean节点创建工厂

 

posted @ 2019-02-25 22:36  葬月!  阅读(197)  评论(0编辑  收藏  举报